/**
 * VMO OpenAI TTS HD Client
 * 
 * Simplified client for OpenAI Text-to-Speech
 * Works with REST API proxy (no direct API key exposure)
 * 
 * @package VMO_AI_Website_Builder
 * @since 1.0.0
 */

(function(){
  'use strict';

  // Abort controller for current TTS network activity (enables true barge-in)
  let VMO_TTS_ABORT = null;


  function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, Math.max(0, ms || 0)));
  }

  // Global function to speak text
  async function vmoSpeak(text, voice='ash', format='mp3', speed=1.0, options){
    // Back-compat: vmoSpeak(text, voice, format, options)
    let opts = options;
    let spd = speed;
    if (speed && typeof speed === 'object' && !Array.isArray(speed)) {
      opts = speed;
      spd = 1.0;
    }
    opts = (opts && typeof opts === 'object') ? opts : {};

    if (!text || !text.trim()) {
      console.warn('VMO TTS: No text provided');
      return null;
    }

    // Create fresh abort controller for this speak
    try { if (VMO_TTS_ABORT) { VMO_TTS_ABORT.abort(); } } catch(_) {}
    VMO_TTS_ABORT = new AbortController();

    // Ensure only one TTS stream plays at a time
    try {
      const existing = document.getElementById('vmo-tts-container');
      if (existing) {
        existing.querySelectorAll('audio').forEach(a => {
          try { a.pause(); } catch(_) {}
          try { a.currentTime = 0; } catch(_) {}
          try { a.removeAttribute('src'); } catch(_) {}
          try { a.load(); } catch(_) {}
          try { a.remove(); } catch(_) {}
        });
      }
      // If something had marked the coordinator as speaking, clear it now.
      try { window.VMOAudioCoordinator && window.VMOAudioCoordinator.onTTSStop(); } catch(_) {}
    } catch(_) {}

    const restUrl = (window.wpApiSettings?.root || '/wp-json/') + 'vmo/v1/tts/speak';
    const nonce = window.wpApiSettings?.nonce || '';

    const mimeMap = {
      mp3: 'audio/mpeg',
      // OpenAI "opus" comes in an Ogg container; use the widely supported type
      opus: 'audio/ogg; codecs=opus',
      aac: 'audio/aac',
      flac: 'audio/flac',
      wav: 'audio/wav',
      pcm: 'audio/pcm'
    };

    // Prefer broadly supported formats first; AAC (ADTS) is poorly supported in <audio>
    const fallbackOrder = ['mp3','wav','opus'];

    const maxAttempts = Number.isFinite(opts.maxAttempts) ? Math.max(1, opts.maxAttempts) : 2;
    const perFetchTimeoutMs = Number.isFinite(opts.timeoutMs) ? Math.max(1000, opts.timeoutMs) : 30000;
    const allowStreaming = opts.allowStreaming !== false;

    async function attemptSpeak(fmt, tried = new Set()){
      tried.add(fmt);
      try {
        console.log('VMO TTS: Requesting speech synthesis...', {text: text.substring(0, 50) + '...', voice, format: fmt});
        
        // Try STREAMING first via admin-ajax to enable progressive playback
        // SKIP streaming for long text (>1500 chars) — GET URLs exceed server limits
        // and cause HTML error responses. Fall through to POST-based REST instead.
        const textLen = text.trim().length;
        if (allowStreaming && textLen <= 1500) try {
          const ajaxUrl = (window.vmo_ajax?.ajaxurl || '/wp-admin/admin-ajax.php');
          const streamUrl = ajaxUrl + `?action=vmo_tts_stream&voice=${encodeURIComponent(voice)}&format=${encodeURIComponent(fmt)}&text=${encodeURIComponent(text.trim())}`;

          for (let attempt = 1; attempt <= maxAttempts; attempt++) {
            // OPTIMIZATION: Removed redundant fetch pre-check that was causing double-generation latency.
            // We trust the audio element to load the stream and handle errors via events.
            
            const audioStream = document.createElement('audio');
            audioStream.preload = 'auto';
            audioStream.autoplay = true;
            audioStream.muted = false;
            audioStream.volume = 1.0;
            audioStream.playsInline = true;
            audioStream.setAttribute('playsinline', '');
            audioStream.src = streamUrl;

            // Provide a stop hook so coordinator barge-in can abort immediately.
            audioStream.vmoStop = () => {
              try { if (VMO_TTS_ABORT) { VMO_TTS_ABORT.abort(); } } catch(_) {}

              try { audioStream.pause(); } catch(_) {}
              try { audioStream.currentTime = 0; } catch(_) {}
              try { audioStream.removeAttribute('src'); } catch(_) {}
              try { audioStream.load(); } catch(_) {}
              try { audioStream.remove(); } catch(_) {}
            };

            let container = document.getElementById('vmo-tts-container');
            if (!container) {
              container = document.createElement('div');
              container.id = 'vmo-tts-container';
              container.style.cssText = 'position:fixed;width:1px;height:1px;overflow:hidden;opacity:0;pointer-events:none;bottom:0;right:0';
              document.body.appendChild(container);
            }
            container.appendChild(audioStream);

            const startupTimeout = Math.min(12000, 5000 + (attempt - 1) * 3000);
            const started = await new Promise((resolve) => {
              let resolved = false;
              const done = (ok) => { if (!resolved) { resolved = true; cleanup(); resolve(ok); } };
              const onPlaying = () => done(true);
              const onError = () => done(false);
              const cleanup = () => {
                audioStream.removeEventListener('playing', onPlaying);
                audioStream.removeEventListener('error', onError);
              };
              audioStream.addEventListener('playing', onPlaying);
              audioStream.addEventListener('error', onError);
              setTimeout(() => done(false), startupTimeout);
              try {
                const p = audioStream.play();
                if (p && typeof p.catch === 'function') {
                  p.catch(err => {
                    if (err && err.name === 'AbortError') return;
                    console.warn('VMO TTS: stream play() failed', err);
                  });
                }
              } catch(_) {}
            });

            if (started) {
              try { window.VMOAudioCoordinator && window.VMOAudioCoordinator.onTTSStart('stream'); } catch(_) {}
              const onStop = () => { try { window.VMOAudioCoordinator && window.VMOAudioCoordinator.onTTSStop(); } catch(_) {} };
              audioStream.addEventListener('ended', onStop, { once: true });
              audioStream.addEventListener('error', onStop, { once: true });
              audioStream.addEventListener('pause', onStop, { once: true });
              return audioStream;
            }

            console.warn('VMO TTS: Streaming did not start, retrying…', { attempt, startupTimeout });
            try { audioStream.vmoStop && audioStream.vmoStop('startup-timeout'); } catch(_) {}
            await sleep(250 * attempt);
          }
        } catch(_streamErr) {
          // Ignore and fall back to admin-ajax POST / REST
        }

        // ─── ADMIN-AJAX POST FALLBACK ───────────────────────────────
        // Uses cookie auth (no REST nonce dependency, no permalink routing).
        // Works for ALL text lengths (no URL limit like GET streaming).
        // This is the most reliable path when REST API returns HTML.
        try {
          const ajPostUrl = (window.vmo_ajax?.ajaxurl || window.vmoAjax?.ajaxurl || '/wp-admin/admin-ajax.php');
          const ajController = new AbortController();
          const ajTimeoutId = setTimeout(() => { try { ajController.abort(); } catch(_){} }, perFetchTimeoutMs);
          try {
            VMO_TTS_ABORT.signal.addEventListener('abort', () => {
              try { ajController.abort(); } catch(_) {}
            }, { once: true });
          } catch(_) {}

          const ajResp = await fetch(ajPostUrl, {
            method: 'POST',
            credentials: 'same-origin',
            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
            body: new URLSearchParams({
              action: 'vmo_tts_stream',
              text: text.trim(),
              voice: voice,
              format: fmt
            }).toString(),
            signal: ajController.signal
          });

          clearTimeout(ajTimeoutId);
          const ajCt = (ajResp.headers.get('Content-Type') || '').toLowerCase();
          if (ajResp.ok && ajCt.startsWith('audio/')) {
            const buffer = await ajResp.arrayBuffer();
            const blob = new Blob([buffer], { type: mimeMap[fmt] || 'audio/mpeg' });
            if (blob.size >= 100) {
              const url = URL.createObjectURL(blob);
              console.log(`VMO TTS: Audio via admin-ajax POST (${blob.size} bytes, ${blob.type})`);

              const audio = document.createElement('audio');
              audio.src = url;
              audio.preload = 'auto';
              audio.autoplay = false;
              audio.muted = false;
              audio.volume = 1.0;
              audio.playsInline = true;
              audio.setAttribute('playsinline', '');
              audio.controls = false;

              audio.vmoStop = () => {
                try { ajController.abort(); } catch(_) {}
                try { audio.pause(); } catch(_) {}
                try { audio.currentTime = 0; } catch(_) {}
                try { audio.removeAttribute('src'); } catch(_) {}
                try { audio.load(); } catch(_) {}
                try { audio.remove(); } catch(_) {}
                try { URL.revokeObjectURL(url); } catch(_) {}
              };

              let container = document.getElementById('vmo-tts-container');
              if (!container) {
                container = document.createElement('div');
                container.id = 'vmo-tts-container';
                container.style.cssText = 'position:fixed;width:1px;height:1px;overflow:hidden;opacity:0;pointer-events:none;bottom:0;right:0;';
                document.body.appendChild(container);
              }
              container.appendChild(audio);
              try { audio.load(); } catch(_e) {}

              const cleanup = () => {
                try { URL.revokeObjectURL(url); } catch(_) {}
                try { audio.remove(); } catch(_) {}
              };
              audio.addEventListener('ended', cleanup, { once: true });
              audio.addEventListener('error', cleanup, { once: true });

              await new Promise((resolve) => {
                let resolved = false;
                const done = () => { if (!resolved) { resolved = true; resolve(); } };
                audio.addEventListener('loadedmetadata', done, { once: true });
                audio.addEventListener('canplaythrough', done, { once: true });
                setTimeout(done, 2000);
              });

              try {
                try { window.VMOAudioCoordinator && window.VMOAudioCoordinator.onTTSStart('buffer'); } catch(_) {}
                let stopSignaled = false;
                const signalStop = () => {
                  if (stopSignaled) return;
                  stopSignaled = true;
                  try { window.VMOAudioCoordinator && window.VMOAudioCoordinator.onTTSStop(); } catch(_) {}
                };
                const playPromise = audio.play();
                if (playPromise !== undefined) await playPromise;
                audio.addEventListener('ended', signalStop, { once: true });
                audio.addEventListener('error', signalStop, { once: true });
                audio.addEventListener('pause', signalStop, { once: true });
                setTimeout(signalStop, 90000);
                return audio;
              } catch (err) {
                try { window.VMOAudioCoordinator && window.VMOAudioCoordinator.onTTSStop(); } catch(_) {}
                throw err;
              }
            } else {
              console.warn('VMO TTS: admin-ajax POST returned small response (' + blob.size + 'B), falling through to REST');
            }
          } else {
            let snippet = '';
            try { snippet = (await ajResp.clone().text()).substring(0, 150); } catch(_) {}
            console.warn('VMO TTS: admin-ajax POST failed:', ajResp.status, ajCt, snippet);
          }
        } catch(_ajErr) {
          if (_ajErr && _ajErr.name === 'AbortError') throw _ajErr;
          console.warn('VMO TTS: admin-ajax POST error:', _ajErr.message);
        }

        // ─── REST POST FALLBACK ─────────────────────────────────────
        // Uses X-WP-Nonce header; requires working REST API routing.
        let lastErr;
        for (let attempt = 1; attempt <= maxAttempts; attempt++) {
          const controller = new AbortController();
          const timeoutId = setTimeout(() => {
            try { controller.abort(); } catch(_) {}
          }, perFetchTimeoutMs);

          try {
            // Tie the per-request abort controller to the global TTS abort controller
            // so barge-in can stop an in-flight request instantly.
            try {
              VMO_TTS_ABORT.signal.addEventListener('abort', () => {
                try { controller.abort(); } catch(_) {}
              }, { once: true });
            } catch(_) {}

            const response = await fetch(restUrl, {
              method: 'POST',
              credentials: 'same-origin',  // Important: send cookies for WordPress auth
              headers: {
                'Content-Type': 'application/json',
                'X-WP-Nonce': nonce
              },
              body: JSON.stringify({
                text: text.trim(),
                voice,
                format: fmt,
                speed: spd
              }),
              signal: controller.signal
            });

            // Quick sanity: require audio/* content-type
            const respType = (response.headers.get('Content-Type') || '').toLowerCase();
            if (!response.ok || !respType.startsWith('audio/')) {
              let errorText = '';
              try { errorText = await response.clone().text(); } catch(_e) {}
              let msg = '';
              try { const j = JSON.parse(errorText); msg = j?.message || j?.error?.message || ''; } catch(_e) {}
              const errMsg = `TTS failed (${response.status} ${response.statusText})` + (msg ? `: ${msg}` : '') + (respType ? ` [type=${respType}]` : '');
              const err = new Error(errMsg);
              err.status = response.status;
              err.contentType = respType;
              throw err;
            }

            const buffer = await response.arrayBuffer();
            const blob = new Blob([buffer], { type: mimeMap[fmt] || 'audio/mpeg' });
            const url = URL.createObjectURL(blob);

            console.log(`VMO TTS: Audio generated successfully (${blob.size} bytes, ${blob.type})`);

            const audio = document.createElement('audio');
            audio.src = url;
            audio.preload = 'auto';
            audio.autoplay = false;
            audio.muted = false;
            audio.volume = 1.0;
            audio.playsInline = true;
            audio.setAttribute('playsinline', '');
            audio.controls = false;

            // Attach cancellation hook for barge-in
            audio.vmoStop = () => {
              try { controller.abort(); } catch(_) {}
              try { audio.pause(); } catch(_) {}
              try { audio.currentTime = 0; } catch(_) {}
              try { audio.removeAttribute('src'); } catch(_) {}
              try { audio.load(); } catch(_) {}
              try { audio.remove(); } catch(_) {}
              try { URL.revokeObjectURL(url); } catch(_) {}
            };

            let container = document.getElementById('vmo-tts-container');
            if (!container) {
              container = document.createElement('div');
              container.id = 'vmo-tts-container';
              container.style.cssText = 'position:fixed;width:1px;height:1px;overflow:hidden;opacity:0;pointer-events:none;bottom:0;right:0;';
              document.body.appendChild(container);
            }
            container.appendChild(audio);

            try { audio.load(); } catch(_e) {
              console.warn('VMO TTS: audio.load() failed', _e);
            }

            const cleanup = () => {
              clearTimeout(timeoutId);
              try { URL.revokeObjectURL(url); } catch(_) {}
              try { audio.remove(); } catch(_) {}
            };
            audio.addEventListener('ended', cleanup, { once: true });
            audio.addEventListener('error', (e) => {
              console.error('VMO TTS: <audio> element error', { code: audio.error?.code, message: audio.error?.message });
              cleanup();
            }, { once: true });

            await new Promise((resolve) => {
              let resolved = false;
              const done = () => { if (!resolved) { resolved = true; resolve(); } };
              const onMeta = () => { audio.removeEventListener('loadedmetadata', onMeta); done(); };
              const onCPT = () => { audio.removeEventListener('canplaythrough', onCPT); done(); };
              const onPlaying = () => { audio.removeEventListener('playing', onPlaying); done(); };
              audio.addEventListener('loadedmetadata', onMeta);
              audio.addEventListener('canplaythrough', onCPT);
              audio.addEventListener('playing', onPlaying);
              setTimeout(done, 2000);
            });

            try {
              try { window.VMOAudioCoordinator && window.VMOAudioCoordinator.onTTSStart('buffer'); } catch(_) {}

              let stopSignaled = false;
              const signalStop = () => {
                if (stopSignaled) return;
                stopSignaled = true;
                try { window.VMOAudioCoordinator && window.VMOAudioCoordinator.onTTSStop(); } catch(_) {}
              };

              const playPromise = audio.play();
              if (playPromise !== undefined) {
                await playPromise;
              }

              audio.addEventListener('ended', signalStop, { once: true });
              audio.addEventListener('error', signalStop, { once: true });
              audio.addEventListener('pause', signalStop, { once: true });
              audio.addEventListener('stalled', signalStop, { once: true });
              audio.addEventListener('abort', signalStop, { once: true });
              setTimeout(signalStop, 90000);
              return audio;
            } catch (err) {
              console.error('VMO TTS: Playback error:', err.name, err.message, err);
              try { window.VMOAudioCoordinator && window.VMOAudioCoordinator.onTTSStop(); } catch(_) {}
              throw err;
            } finally {
              clearTimeout(timeoutId);
            }
          } catch (err) {
            clearTimeout(timeoutId);
            lastErr = err;
            const aborted = err && (err.name === 'AbortError');
            const status = err && err.status;
            const retryableStatus = status === 429 || status === 500 || status === 502 || status === 503 || status === 504;
            if (!aborted && (retryableStatus || !status)) {
              console.warn('VMO TTS: REST attempt failed, retrying…', { attempt, status, message: err.message });
              await sleep(250 * attempt);
              continue;
            }
            throw err;
          }
        }

        throw lastErr || new Error('TTS failed');
      } catch (error) {
        // Config/API errors should NOT retry with different formats - immediately throw
        if (error.isConfigError || error.isApiKeyError) {
          console.warn('VMO TTS: Config error detected, skipping format retries');
          throw error;
        }
        
        // On network or transient errors, try fallback formats
        if (tried.size < fallbackOrder.length) {
          const next = fallbackOrder.find(f => !tried.has(f));
          if (next) {
            console.warn('VMO TTS: Retrying with fallback format', { from: fmt, to: next });
            return attemptSpeak(next, tried);
          }
        }
        throw error;
      }
    }

    try {
      return await attemptSpeak(format);
    } catch (error) {
      console.error('VMO TTS Error:', error);
      throw error;
    }
  }

  // Expose globally
  window.vmoSpeak = vmoSpeak;

  /**
   * Auto-play TTS for elements with data-vmo-tts attribute
   * 
   * Usage:
   * <button data-vmo-tts="Hello world!" data-vmo-voice="alloy">Speak</button>
   * <a href="#" data-vmo-tts="Custom message">Click to hear</a>
   */
  document.addEventListener('click', async (e) => {
    const el = e.target.closest('[data-vmo-tts]');
    if (!el) return;

    e.preventDefault();

    const text = el.getAttribute('data-vmo-tts') || el.textContent.trim();
    const voice = el.getAttribute('data-vmo-voice') || 'ash';
    const format = el.getAttribute('data-vmo-format') || 'mp3';
    const speed = parseFloat(el.getAttribute('data-vmo-speed') || '1.0');

    if (!text) {
      console.warn('VMO TTS: No text found for element', el);
      return;
    }

    try {
      // Show loading state if element has a class for it
      el.classList.add('vmo-tts-loading');
      
      await vmoSpeak(text, voice, format, speed);
      
      // Remove loading state
      el.classList.remove('vmo-tts-loading');
      el.classList.add('vmo-tts-success');
      
      setTimeout(() => el.classList.remove('vmo-tts-success'), 2000);

    } catch (error) {
      el.classList.remove('vmo-tts-loading');
      el.classList.add('vmo-tts-error');
      setTimeout(() => el.classList.remove('vmo-tts-error'), 2000);
    }
  });

  /**
   * Get available voices
   */
  async function vmoGetVoices() {
    try {
      // Use admin-ajax.php for reliability (REST API permalinks can be flaky)
      const ajaxUrl = (window.vmo_ajax?.ajaxurl || '/wp-admin/admin-ajax.php');
      const url = ajaxUrl + '?action=vmo_tts_voices';
      
      const response = await fetch(url);

      if (!response.ok) {
        throw new Error('Failed to fetch voices');
      }

      const data = await response.json();
      return data.voices || [];

    } catch (error) {
      console.error('VMO TTS: Error fetching voices:', error);
      // Fallback to static list if fetch fails
      return [
        { id: 'ash', name: 'Ash', description: 'Modern, confident tone' },
        { id: 'alloy', name: 'Alloy', description: 'Neutral and balanced' },
        { id: 'echo', name: 'Echo', description: 'Warm and engaging' },
        { id: 'fable', name: 'Fable', description: 'Expressive and dynamic' },
        { id: 'onyx', name: 'Onyx', description: 'Deep and authoritative' },
        { id: 'nova', name: 'Nova', description: 'Bright and friendly' },
        { id: 'shimmer', name: 'Shimmer', description: 'Clear and articulate' }
      ];
    }
  }

  // Expose voice fetcher globally
  window.vmoGetVoices = vmoGetVoices;

  console.log('✅ VMO TTS HD initialized');

})();
