// translate.jsx — Real-time STT + Translation engine
// STT: browser Web Speech API (free, same backend as Google Cloud)
// Translation: MyMemory API (free, 1000 words/day, no key needed)

// All 12 app languages → BCP-47 locale for Web Speech API
const STT_LOCALE = {
  es: 'es-ES', en: 'en-US', fr: 'fr-FR', de: 'de-DE',
  it: 'it-IT', pt: 'pt-PT', zh: 'zh-CN', ru: 'ru-RU',
  nl: 'nl-NL', ja: 'ja-JP', ar: 'ar-SA', pl: 'pl-PL',
};

// ── Speech-to-Text via browser Web Speech API ─────────────────────────────────
let _recognition = null;

function startListening({ lang, onPartial, onFinal, onError, onEnd }) {
  const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
  if (!SR) { onError && onError('STT no soportado'); return null; }

  // Always kill any existing session cleanly before starting a new one
  // (removes the _isStarting race-condition that caused the mic to get stuck)
  if (_recognition) {
    const old = _recognition;
    _recognition = null;
    old.onend = null; // prevent stale callbacks
    try { old.abort(); } catch(e) {}
  }

  const rec = new SR();
  rec.lang = STT_LOCALE[lang] || 'en-US';
  rec.continuous     = true;  // keeps going until stopListening()
  rec.interimResults = true;
  rec.maxAlternatives = 1;

  let finalText = '';

  rec.onresult = (e) => {
    let interim = '';
    for (let i = e.resultIndex; i < e.results.length; i++) {
      const t = e.results[i][0].transcript;
      if (e.results[i].isFinal) finalText += t;
      else interim += t;
    }
    if (interim && onPartial) onPartial(interim);
    if (finalText && onFinal)  onFinal(finalText);
  };

  rec.onerror = (e) => {
    if (_recognition === rec) _recognition = null;
    console.warn('[STT error]', e.error);
    if (e.error !== 'aborted' && onError) onError(e.error);
  };

  // onend fires both on abort and on natural end — notify React so UI stays in sync
  rec.onend = () => {
    if (_recognition === rec) _recognition = null;
    if (onEnd) onEnd();
  };

  try {
    rec.start();
    _recognition = rec;
  } catch(e) {
    _recognition = null;
    onError && onError(e.message);
  }
}

function stopListening() {
  if (_recognition) {
    const r = _recognition;
    _recognition = null;
    r.onend = null; // silence the onEnd callback so it doesn't trigger a spurious state update
    try { r.abort(); } catch(e) {}
  }
}

// ── Translation — primary: Google (unofficial, no key, no limit)
//                 fallback: MyMemory+email (10 000 words/day free)  ───────────
const _translationCache = new Map();

// Unofficial Google Translate endpoint used by many open-source tools.
// Returns the translated string or null on any error.
async function _googleTranslate(text, fromLang, toLang) {
  try {
    const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=${fromLang}&tl=${toLang}&dt=t&q=${encodeURIComponent(text)}`;
    const res  = await fetch(url, { signal: AbortSignal.timeout(7000) });
    if (!res.ok) return null;
    const data = await res.json();
    // Response structure: [ [ ["translated","original",...], … ], … ]
    const parts = data?.[0] || [];
    const result = parts.map(p => p?.[0] || '').join('').trim();
    return result || null;
  } catch { return null; }
}

// MyMemory with registered email — 10 000 words/day (vs 1 000 anonymous).
async function _myMemoryTranslate(text, fromLang, toLang) {
  try {
    const url = `https://api.mymemory.translated.net/get?q=${encodeURIComponent(text)}&langpair=${fromLang}|${toLang}&de=edeforteza@gmail.com`;
    const res  = await fetch(url, { signal: AbortSignal.timeout(9000) });
    const data = await res.json();
    const result = data?.responseData?.translatedText || '';
    // Detect the daily-limit warning message and treat it as a failure
    if (!result || result.includes('MYMEMORY WARNING') || result.includes('PLEASE REGISTER')) return null;
    return result;
  } catch { return null; }
}

async function translate(text, fromLang, toLang) {
  if (!text || fromLang === toLang) return text;
  const key = `${fromLang}|${toLang}|${text}`;
  if (_translationCache.has(key)) return _translationCache.get(key);

  // Try Google first; fall back to MyMemory; fall back to original text
  let result = await _googleTranslate(text, fromLang, toLang);
  if (!result) result = await _myMemoryTranslate(text, fromLang, toLang);
  if (!result) { console.warn('[translate] both APIs failed, returning original'); return text; }

  _translationCache.set(key, result);
  return result;
}

// ── PubNub publisher — sends events to driver's phone ────────────────────────
let _pubnub = null;

function getPubNub() {
  if (_pubnub) return _pubnub;
  const pub = window.__PUBNUB_PUB || '';
  const sub = window.__PUBNUB_SUB || '';
  if (!pub || !sub) return null;
  _pubnub = new PubNub({
    publishKey:   pub,
    subscribeKey: sub,
    userId: 'tablet-' + (window.__SESSION_ID || 'demo'),
  });
  return _pubnub;
}

function publishToDriver(msg) {
  const pn      = getPubNub();
  const channel = window.__SESSION_ID || 'taxi-malaga-1';
  if (!pn) return; // PubNub not configured — silent fail
  pn.publish({ channel, message: msg }).catch(() => {});
}

// Call this when passenger selects language
function notifyLangSelected(lang) {
  publishToDriver({ type: 'lang_selected', lang });
}

// Call this when translation completes
function publishTranslation({ role, source, target, srcLang, tgtLang }) {
  publishToDriver({ type: 'translation', role, source, target, srcLang, tgtLang });
}

// ── PubNub subscriber — tablet listens for driver speech ─────────────────────
function initTabletSubscriber() {
  const pn = getPubNub();
  if (!pn) return;
  const channel = window.__SESSION_ID || 'taxi-malaga-1';

  pn.addListener({
    message: (evt) => {
      const msg = evt.message;
      if (!msg) return;

      if (msg.type === 'admin_update') {
        window.dispatchEvent(new CustomEvent('admin_update', { detail: msg }));
        return;
      }
      if (msg.type === 'ride_start') {
        window.dispatchEvent(new CustomEvent('ride_start', { detail: { destination: msg.destination || null } }));
        return;
      }
      if (msg.type === 'ride_end') {
        window.dispatchEvent(new CustomEvent('ride_end', { detail: { fare: msg.fare || null } }));
        return;
      }

      if (msg.type !== 'driver_speech') return;

      // Driver spoke Spanish → Leo says it in passenger's language
      const text = msg.target;
      const lang = msg.tgtLang || 'en';

      // Show on tablet screen via custom event
      window.dispatchEvent(new CustomEvent('driver_message', { detail: { source: msg.source, target: text, lang } }));

      // Speak via Leo (ElevenLabs if available, else browser TTS)
      if (window.speak) {
        window.speak(text, lang, { volume: 1 });
      }
    },
  });

  pn.subscribe({ channels: [channel] });
  console.log('[PubNub tablet] subscribed to', channel);
}

// Init subscriber after a short delay to let PubNub load
setTimeout(initTabletSubscriber, 2000);

Object.assign(window, { startListening, stopListening, translate, publishTranslation, notifyLangSelected });
