/*
  PLAYER — plays a stream in a <video> element and supports switching
  sources mid-watch without losing your place.

  Pipeline per stream:
    - hls.js for .m3u8 when available
    - native HLS for Safari (canPlayType check)
    - direct <video src> for mp4/webm
    - everything else (DASH, mkv, magnet, …) → friendly error

  Every media + subtitle URL is routed through the API proxy.
  On source switch we carry over currentTime, playbackRate, volume, muted
  and the playing state, clamped to the new duration.
*/
(function () {
  'use strict';

  function create(video, handlers = {}) {
    const onStatus = handlers.onStatus || (() => {});
    const onError = handlers.onError || (() => {});
    const onReady = handlers.onReady || (() => {});

    let hls = null;
    let token = 0; // guards against overlapping loads
    let networkRetry = false;

    function teardown() {
      token++;
      if (hls) {
        try {
          hls.destroy();
        } catch (e) {
          /* already destroyed */
        }
        hls = null;
      }
      for (const track of video.querySelectorAll('track')) track.remove();
      video.removeAttribute('src');
      try {
        video.load();
      } catch (e) {
        /* ignore */
      }
    }

    function capture() {
      const v = video;
      return {
        time: isFinite(v.currentTime) && v.currentTime > 0 ? v.currentTime : 0,
        rate: v.playbackRate || 1,
        volume: v.volume,
        muted: v.muted,
        playing: !v.paused && !v.ended,
      };
    }

    function restore(st) {
      const v = video;
      v.playbackRate = st.rate;
      v.volume = st.volume;
      v.muted = st.muted;
      const seek = () => {
        const d = v.duration;
        let t = st.time;
        if (isFinite(d) && d > 0) t = Math.min(t, Math.max(0, d - 5));
        if (t > 10) v.currentTime = t;
        if (st.playing) v.play().catch(() => {});
      };
      if (isFinite(v.duration) && v.duration > 0) seek();
      else v.addEventListener('loadedmetadata', seek, { once: true });
    }

    function attachSubtitles(stream) {
      for (const sub of stream.subtitles || []) {
        let resolved;
        try {
          resolved = new URL(sub.url, stream.url).toString();
        } catch (e) {
          continue;
        }
        const track = document.createElement('track');
        track.kind = 'subtitles';
        track.srclang = sub.language || 'en';
        track.label = sub.name || sub.language || 'Subtitles';
        track.src = SR.api.proxyUrl(resolved, stream.headers);
        video.appendChild(track);
      }
    }

    function load(stream, opts = {}) {
      const st = capture();
      const explicitResume = typeof opts.resumeAt === 'number' && opts.resumeAt > 10;
      if (typeof opts.resumeAt === 'number') st.time = Math.max(0, opts.resumeAt);
      const readyAt = () => (explicitResume ? st.time : 0);

      teardown();
      networkRetry = false;
      const myToken = token;
      const guarded = (fn) => () => {
        if (myToken === token) fn();
      };

      const url = stream.url || '';
      const isHls = stream.type === 'm3u8' || /\.m3u8($|\?)/i.test(url);
      const isDash = stream.type === 'dash' || /\.mpd($|\?)/i.test(url);
      const isDirect =
        stream.type === 'mp4' ||
        stream.type === 'webm' ||
        /\.(mp4|m4v|webm)(\?|#|$)/i.test(url);

      if (isDash) {
        onError('DASH streams are not supported yet — pick another source.');
        return;
      }
      if (!isHls && !isDirect) {
        onError(`The browser can't play ${stream.type} files directly — pick an MP4 or HLS source.`);
        return;
      }

      attachSubtitles(stream);
      const proxied = SR.api.proxyUrl(url, stream.headers);

      if (isHls && window.Hls && Hls.isSupported()) {
        onStatus('Loading stream…');
        const h = new Hls({ enableWorker: true });
        hls = h;
        h.loadSource(proxied);
        h.attachMedia(video);
        h.on(Hls.Events.MANIFEST_PARSED, guarded(() => {
          restore(st);
          onReady(readyAt());
        }));
        h.on(Hls.Events.ERROR, (evt, data) => {
          if (myToken !== token || !data.fatal) return;
          if (data.type === Hls.ErrorTypes.NETWORK_ERROR) {
            if (!networkRetry) {
              networkRetry = true;
              setTimeout(() => {
                networkRetry = false;
                if (myToken === token && hls === h) h.startLoad();
              }, 2000);
            }
          } else if (data.type === Hls.ErrorTypes.MEDIA_ERROR) {
            h.recoverMediaError();
          } else {
            onError('Playback failed: ' + (data.details || 'unknown stream error'));
            teardown();
          }
        });
        return;
      }

      // native HLS (Safari) or direct file
      onStatus('Loading stream…');
      video.src = proxied;
      video.addEventListener(
        'loadedmetadata',
        guarded(() => {
          restore(st);
          onReady(readyAt());
        }),
        { once: true }
      );
      video.addEventListener(
        'error',
        guarded(() => {
          onError('Could not load this stream — try another source.');
        }),
        { once: true }
      );
    }

    function destroy() {
      teardown();
    }

    return { load, destroy };
  }

  window.SR = window.SR || {};
  SR.player = { create };
})();
