/* /functional/audio/global_player.js
   - глобальний аудіо-плеєр (queue, prev/next, auto-next)
   - PJAX (музика не зупиняється)
   - Close: зупинити і сховати плеєр
   - FIX: синхронізація CSS з <head> при PJAX (+ cleanup)
   - IMPROVE: abort previous PJAX fetch, loading-state, custom events
   - FIX: cleanup глобальних document/window listeners, які додають inline-скрипти сторінок після PJAX
   - FIX: для фотоальбомів PJAX вимкнено повністю, щоб не було стрибка скролу
*/

(function () {
  const dock = document.getElementById('vmAudioDock');
  if (!dock) return;

  const elAudio  = document.getElementById('vmGA_Audio');
  const elTitle  = document.getElementById('vmGA_Title');
  const elArtist = document.getElementById('vmGA_Artist');

  const elPlay  = document.getElementById('vmGA_Play');
  const elPrev  = document.getElementById('vmGA_Prev');
  const elNext  = document.getElementById('vmGA_Next');
  const elClose = document.getElementById('vmGA_Close');

  const elSeek = document.getElementById('vmGA_Seek');
  const elCur  = document.getElementById('vmGA_Cur');
  const elDur  = document.getElementById('vmGA_Dur');
  const elVol  = document.getElementById('vmGA_Vol');

  const main = document.getElementById('appMain');

  if (!elAudio || !elTitle || !elArtist || !elPlay || !elPrev || !elNext || !elSeek || !elCur || !elDur || !elVol) {
    return;
  }

  const LS_QUEUE = 'vmga_queue_v1';
  const LS_INDEX = 'vmga_index_v1';
  const LS_VOL   = 'vmga_vol_v1';

  const PJAX_HEADER = { 'X-VM-PJAX': '1' };

  const cssEscape = (s) => {
    try {
      if (window.CSS && typeof CSS.escape === 'function') return CSS.escape(String(s));
    } catch (e) {}
    return String(s).replace(/["\\]/g, '\\$&');
  };

  function fmtTime(sec) {
    sec = Math.max(0, Math.floor(sec || 0));
    const m = Math.floor(sec / 60);
    const s = sec % 60;
    return m + ':' + String(s).padStart(2, '0');
  }

  function showDock() {
    dock.classList.remove('d-none');
    document.body.classList.add('has-audio-player');
  }

  function hideDock() {
    dock.classList.add('d-none');
    document.body.classList.remove('has-audio-player');
  }

  function hideDockIfEmpty(queue) {
    if (!queue || !queue.length) hideDock();
  }

  function dispatchEvt(name, detail) {
    try {
      document.dispatchEvent(new CustomEvent(name, { detail: detail || {} }));
    } catch (e) {}
  }

  async function apiJSON(url) {
    const r = await fetch(url, {
      credentials: 'same-origin',
      headers: Object.assign({ 'Accept': 'application/json' }, PJAX_HEADER)
    });
    if (!r.ok) throw new Error('HTTP ' + r.status);

    const ct = (r.headers.get('content-type') || '').toLowerCase();
    if (!ct.includes('application/json')) throw new Error('not_json');

    return await r.json();
  }

  const VMGA = {
    queue: [],
    index: 0,

    saveState() {
      try {
        localStorage.setItem(LS_QUEUE, JSON.stringify(this.queue || []));
        localStorage.setItem(LS_INDEX, String(this.index || 0));
        localStorage.setItem(LS_VOL, String(Math.round((elAudio.volume || 0) * 100)));
      } catch (e) {}
    },

    loadState() {
      try {
        const q = JSON.parse(localStorage.getItem(LS_QUEUE) || '[]');
        const i = parseInt(localStorage.getItem(LS_INDEX) || '0', 10);
        const v = parseInt(localStorage.getItem(LS_VOL) || '80', 10);

        if (Array.isArray(q)) this.queue = q;
        if (!Number.isNaN(i)) this.index = Math.max(0, i);

        const vv = Math.max(0, Math.min(100, Number.isNaN(v) ? 80 : v));
        elAudio.volume = vv / 100;
        elVol.value = String(vv);
      } catch (e) {}
    },

    clearState() {
      try {
        localStorage.removeItem(LS_QUEUE);
        localStorage.removeItem(LS_INDEX);
      } catch (e) {}
    },

    current() {
      return this.queue[this.index] || null;
    },

    loadCurrent(autoplay) {
      const t = this.current();
      if (!t) {
        elTitle.textContent = '—';
        elArtist.textContent = '';
        elAudio.removeAttribute('src');
        try { elAudio.load(); } catch (e) {}
        hideDockIfEmpty(this.queue);
        return;
      }

      showDock();
      elTitle.textContent = t.title || '—';
      elArtist.textContent = t.artist || '';
      elAudio.src = t.url;
      try { elAudio.load(); } catch (e) {}

      if (autoplay) elAudio.play().catch(() => {});
      this.saveState();
    },

    setQueue(queue, index, autoplay = true) {
      this.queue = Array.isArray(queue) ? queue : [];
      const max = Math.max(0, this.queue.length - 1);
      this.index = Math.max(0, Math.min(max, index || 0));
      this.saveState();
      this.loadCurrent(autoplay);
    },

    toggle() {
      if (!this.current()) return;
      if (elAudio.paused) elAudio.play().catch(() => {});
      else elAudio.pause();
    },

    next() {
      if (!this.queue.length) return;
      if (this.index < this.queue.length - 1) {
        this.index += 1;
        this.loadCurrent(true);
      } else {
        try { elAudio.pause(); } catch (e) {}
        elAudio.currentTime = 0;
      }
      this.saveState();
    },

    prev() {
      if (!this.queue.length) return;
      if (elAudio.currentTime > 3) {
        elAudio.currentTime = 0;
        return;
      }
      if (this.index > 0) {
        this.index -= 1;
        this.loadCurrent(true);
      } else {
        elAudio.currentTime = 0;
      }
      this.saveState();
    },

    stopAndClose() {
      try { elAudio.pause(); } catch (e) {}
      try { elAudio.currentTime = 0; } catch (e) {}

      this.queue = [];
      this.index = 0;

      elAudio.removeAttribute('src');
      try { elAudio.load(); } catch (e) {}

      elTitle.textContent = '—';
      elArtist.textContent = '';
      elSeek.value = '0';
      elCur.textContent = '0:00';
      elDur.textContent = '0:00';

      this.clearState();
      hideDock();
    },

    async playAll() {
      const data = await apiJSON('/functional/audio/api_tracks.php');
      const q = data.tracks || [];
      this.setQueue(q, 0, true);
    },

    async playTrackById(id) {
      const data = await apiJSON('/functional/audio/api_tracks.php');
      const q = data.tracks || [];
      const idx = q.findIndex(x => String(x.id) === String(id));
      this.setQueue(q, Math.max(0, idx), true);
    },

    async playPlaylist(id) {
      const data = await apiJSON('/functional/audio/api_playlist.php?id=' + encodeURIComponent(id));
      const q = data.tracks || [];
      this.setQueue(q, 0, true);
    }
  };

  window.VMGA = VMGA;

  elPlay.addEventListener('click', () => VMGA.toggle());
  elPrev.addEventListener('click', () => VMGA.prev());
  elNext.addEventListener('click', () => VMGA.next());

  if (elClose) {
    elClose.addEventListener('click', () => VMGA.stopAndClose());
  }

  elAudio.addEventListener('play',  () => { elPlay.textContent = '⏸'; });
  elAudio.addEventListener('pause', () => { elPlay.textContent = '▶'; });
  elAudio.addEventListener('ended', () => VMGA.next());

  elAudio.addEventListener('loadedmetadata', () => {
    elDur.textContent = fmtTime(elAudio.duration);
  });

  elAudio.addEventListener('timeupdate', () => {
    const d = elAudio.duration || 0;
    const c = elAudio.currentTime || 0;
    elCur.textContent = fmtTime(c);
    if (d > 0) {
      const v = Math.round((c / d) * 1000);
      elSeek.value = String(Math.max(0, Math.min(1000, v)));
    } else {
      elSeek.value = '0';
    }
  });

  elSeek.addEventListener('input', () => {
    const d = elAudio.duration || 0;
    if (d <= 0) return;
    const v = parseInt(elSeek.value || '0', 10);
    elAudio.currentTime = Math.max(0, Math.min(d, (v / 1000) * d));
  });

  elVol.addEventListener('input', () => {
    const v = Math.max(0, Math.min(100, parseInt(elVol.value || '80', 10)));
    elAudio.volume = v / 100;
    VMGA.saveState();
  });

  VMGA.loadState();
  if (VMGA.queue && VMGA.queue.length && VMGA.current()) {
    showDock();
    VMGA.loadCurrent(false);
  } else {
    hideDockIfEmpty(VMGA.queue);
  }

  if (!main) return;

  const persistentStyles = new Set(
    Array.from(document.head.querySelectorAll('link[rel="stylesheet"][href]')).map(l => {
      const href = l.getAttribute('href') || '';
      try { return new URL(href, location.href).href; } catch (e) { return href; }
    })
  );

  let pjaxController = null;

  const pjaxPageGlobalListeners = [];
  const pjaxInjectedScripts = [];

  function sameOrigin(url) {
    try {
      const u = new URL(url, location.href);
      return u.origin === location.origin;
    } catch (e) { return false; }
  }

  function shouldSkipLink(a, e) {
    if (!a) return true;
    if (e.defaultPrevented) return true;
    if (e.button !== 0) return true;
    if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return true;
    if (a.hasAttribute('download')) return true;
    if (a.getAttribute('rel') && String(a.getAttribute('rel')).toLowerCase().includes('external')) return true;

    const target = (a.getAttribute('target') || '').toLowerCase();
    if (target && target !== '_self') return true;

    if (a.getAttribute('data-no-pjax') === '1') return true;

    const href = a.getAttribute('href') || '';
    if (!href) return true;
    if (href.startsWith('#') || href.startsWith('javascript:')) return true;
    if (href.startsWith('mailto:') || href.startsWith('tel:')) return true;

    if (!sameOrigin(href)) return true;

    const u = new URL(href, location.href);
    const p = u.pathname || '/';

    /* ВАЖЛИВО: для альбомів PJAX вимикаємо, щоб не було ривка */
    const skipPrefixes = ['/admin', '/upload', '/uploads', '/assets', '/functional/albums'];
    if (skipPrefixes.some(x => p.startsWith(x))) return true;

    const skipExact = ['/logout.php', '/login.php', '/register.php', '/set_lang.php'];
    if (skipExact.includes(p)) return true;

    return false;
  }

  function cleanupPageGlobalListeners() {
    while (pjaxPageGlobalListeners.length) {
      const item = pjaxPageGlobalListeners.pop();
      try {
        if (item && item.target && item.type && item.listener) {
          item.target.removeEventListener(item.type, item.listener, item.options);
        }
      } catch (e) {}
    }
  }

  function cleanupInjectedScripts() {
    while (pjaxInjectedScripts.length) {
      const s = pjaxInjectedScripts.pop();
      try { if (s && s.parentNode) s.parentNode.removeChild(s); } catch (e) {}
    }
  }

  function trackTargetListeners(target) {
    const originalAdd = target.addEventListener;
    const originalRemove = target.removeEventListener;

    target.addEventListener = function(type, listener, options) {
      if (typeof listener === 'function' || (listener && typeof listener.handleEvent === 'function')) {
        pjaxPageGlobalListeners.push({ target, type, listener, options });
      }
      return originalAdd.call(this, type, listener, options);
    };

    target.removeEventListener = function(type, listener, options) {
      for (let i = pjaxPageGlobalListeners.length - 1; i >= 0; i--) {
        const it = pjaxPageGlobalListeners[i];
        if (it.target === target && it.type === type && it.listener === listener) {
          pjaxPageGlobalListeners.splice(i, 1);
        }
      }
      return originalRemove.call(this, type, listener, options);
    };

    return function restore() {
      target.addEventListener = originalAdd;
      target.removeEventListener = originalRemove;
    };
  }

  function runScriptsInside(node) {
    const scripts = Array.from(node.querySelectorAll('script'));
    if (!scripts.length) return;

    cleanupPageGlobalListeners();
    cleanupInjectedScripts();

    const restoreDocument = trackTargetListeners(document);
    const restoreWindow = trackTargetListeners(window);

    try {
      scripts.forEach((s) => {
        const ns = document.createElement('script');
        ns.setAttribute('data-vm-pjax-script', '1');

        for (const attr of s.attributes) {
          ns.setAttribute(attr.name, attr.value);
        }

        if (s.src) {
          const src = s.getAttribute('src');
          if (src) {
            const sel = 'script[src="' + cssEscape(src) + '"]';
            if (document.querySelector(sel)) {
              s.remove();
              return;
            }
          }
          ns.src = s.src;
          ns.async = false;
          document.body.appendChild(ns);
          pjaxInjectedScripts.push(ns);
        } else {
          ns.text = s.textContent || '';
          document.body.appendChild(ns);
          pjaxInjectedScripts.push(ns);
        }

        s.remove();
      });
    } finally {
      restoreDocument();
      restoreWindow();
    }
  }

  function syncHeadStyles(doc) {
    try {
      const newLinks = Array.from(doc.querySelectorAll('head link[rel="stylesheet"][href]'));
      const curLinks = Array.from(document.head.querySelectorAll('link[rel="stylesheet"][href]'));

      const newSet = new Set(newLinks.map(l => {
        const href = l.getAttribute('href') || '';
        try { return new URL(href, location.href).href; } catch (e) { return href; }
      }));

      newLinks.forEach(l => {
        const href = l.getAttribute('href');
        if (!href) return;

        let abs = href;
        try { abs = new URL(href, location.href).href; } catch (e) {}

        const already = curLinks.some(x => {
          const ch = x.getAttribute('href') || '';
          try { return new URL(ch, location.href).href === abs; } catch (e) { return ch === abs; }
        });
        if (already) return;

        const nl = document.createElement('link');
        Array.from(l.attributes).forEach(a => nl.setAttribute(a.name, a.value));
        document.head.appendChild(nl);
      });

      curLinks.forEach(l => {
        const href = l.getAttribute('href') || '';
        let abs = href;
        try { abs = new URL(href, location.href).href; } catch (e) {}

        if (persistentStyles.has(abs)) return;
        if (newSet.has(abs)) return;

        try { l.remove(); } catch (e) {}
      });
    } catch (e) {}
  }

  async function pjaxLoad(url, push = true) {
    try { if (pjaxController) pjaxController.abort(); } catch (e) {}
    pjaxController = new AbortController();

    document.body.classList.add('pjax-loading');
    dispatchEvt('vm:pjax:before', { url });

    try {
      const r = await fetch(url, {
        credentials: 'same-origin',
        headers: Object.assign({ 'Accept': 'text/html' }, PJAX_HEADER),
        signal: pjaxController.signal
      });

      if (!r.ok) throw new Error('HTTP ' + r.status);

      const html = await r.text();
      const doc = new DOMParser().parseFromString(html, 'text/html');

      const newMain = doc.getElementById('appMain');
      if (!newMain) {
        location.href = url;
        return;
      }

      syncHeadStyles(doc);

      document.title = doc.title || document.title;
      if (doc.documentElement && doc.documentElement.lang) {
        document.documentElement.lang = doc.documentElement.lang;
      }

      main.innerHTML = newMain.innerHTML;
      runScriptsInside(main);

      if (push) history.pushState({}, '', url);

      window.scrollTo({ top: 0, left: 0, behavior: 'auto' });

      dispatchEvt('vm:pjax:loaded', { url });
    } catch (e) {
      if (e && (e.name === 'AbortError' || String(e).includes('AbortError'))) {
        return;
      }
      dispatchEvt('vm:pjax:error', { url, error: String(e && e.message ? e.message : e) });
      location.href = url;
    } finally {
      document.body.classList.remove('pjax-loading');
    }
  }

  document.addEventListener('click', (e) => {
    const a = e.target && e.target.closest ? e.target.closest('a') : null;
    if (shouldSkipLink(a, e)) return;

    const href = a.href;
    if (!href) return;

    try {
      const u = new URL(href, location.href);
      const sameDoc = (u.pathname === location.pathname && u.search === location.search);
      if (sameDoc && u.hash) {
        e.preventDefault();
        location.hash = u.hash;
        return;
      }
    } catch (err) {}

    e.preventDefault();
    pjaxLoad(href, true);
  });

  window.addEventListener('popstate', () => {
    pjaxLoad(location.href, false);
  });

})();