<template>
  <div class="ali-player-wrapper" :style="{ background: '#000' }">
    <div v-if="showError" class="player-error">{{ errorText }}</div>

    <div ref="playerContainer" class="player-container" :style="{ width: '100%', height: playerHeight }"></div>

    <!-- 倒计时覆盖（可选，父层也可以自行实现） -->
    <div v-if="showCountDown" class="countdown-overlay">
      <div class="countdown-text">{{ countDownSeconds }} 秒后播放下一个视频</div>
      <button class="btn-cancel" @click="cancelNext">取消下一个</button>
    </div>

    <!-- 控制按钮示例（父层应该控制 UI，我仅提供常用API按钮用于调试） -->
    <div class="player-controls" style="display:none;">
      <button @click="play()">播放</button>
      <button @click="pause()">暂停</button>
      <button @click="replay()">重播</button>
      <button @click="enterFullscreen()">全屏</button>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, onMounted, onBeforeUnmount, watch, PropType, nextTick } from 'vue';

type Platform = 'web' | 'app-ios' | 'app-android';

export default defineComponent({
  name: 'AliyunPlayer',
  props: {
    // videoData: should include fields similar to original: type, m3u8Url, videoUrl, videoId, playAuth, firstTime, id...
    videoData: { type: Object as PropType<Record<string, any>>, required: true },
    // platform hint: affects screen lock behavior; default web
    platform: { type: String as PropType<Platform>, default: 'web' },
    // height for player area
    height: { type: String, default: '200px' },
    // auto start playback
    autoplay: { type: Boolean, default: true },
    // how often to auto-save progress in seconds (default 60)
    autoSaveInterval: { type: Number, default: 60 },
    // localStorage key prefix for resume data
    storageKeyPrefix: { type: String, default: 'videoOssList' },
    // flag: in APP environment should use WebView (true) or try to run player directly in page (false)
    useWebViewForApp: { type: Boolean, default: false },
    // urls for loading Aliplayer (allow overriding if needed)
    playerScriptUrl: { type: String, default: 'https://g.alicdn.com/apsara-media-box/imp-web-player/2.20.3/aliplayer-min.js' },
    playerComponentsUrl: { type: String, default: 'https://player.alicdn.com/aliplayer/presentation/js/aliplayercomponents.min.js' },
    playerCssUrl: { type: String, default: 'https://g.alicdn.com/apsara-media-box/imp-web-player/2.20.3/skins/default/aliplayer-min.css' },
  },
  emits: [
    'ready',
    'play',
    'pause',
    'timeupdate',
    'progress-save', // payload: { videoId, position }
    'ended',
    'error',
    'request-playauth', // in case parent wants to fetch playAuth separately
    'change-screen',
    'load-next' // when ended and parent should load next
  ],
  setup(props, { emit, expose }) {
    const playerContainer = ref<HTMLElement | null>(null);
    const playerInstance = ref<any | null>(null);
    const scriptLoaded = ref(false);
    const timerDiff = ref(0);
    const currentSeconds = ref(0);
    const pauseTime = ref(0);
    const saveCounter = ref(0);
    const autoSaveIntervalId = ref<number | null>(null);
    const showCountDown = ref(false);
    const countDownSeconds = ref(5);
    const countdownTimerId = ref<number | null>(null);
    const showError = ref(false);
    const errorText = ref('');

    const playerHeight = props.height;

    // helper: localStorage save/load (simple array of {id, time})
    function loadResumeList(): Array<any> {
      try {
        const raw = localStorage.getItem(props.storageKeyPrefix);
        return raw ? JSON.parse(raw) : [];
      } catch (e) {
        return [];
      }
    }
    function saveResumeItem(videoId: any, time: number) {
      try {
        const list = loadResumeList();
        const idx = list.findIndex((i: any) => i.id === videoId);
        if (idx >= 0) list[idx].time = time;
        else list.push({ id: videoId, time });
        localStorage.setItem(props.storageKeyPrefix, JSON.stringify(list));
      } catch (e) { /* ignore */ }
    }

    // dynamic load aliplayer script + css
    function loadAliplayer(): Promise<void> {
      if ((window as any).Aliplayer) {
        scriptLoaded.value = true;
        return Promise.resolve();
      }
      return new Promise((resolve, reject) => {
        // css
        const link = document.createElement('link');
        link.rel = 'stylesheet';
        link.href = props.playerCssUrl;
        document.head.appendChild(link);

        // main script
        const s = document.createElement('script');
        s.src = props.playerScriptUrl;
        s.onload = () => {
          // components script
          const s2 = document.createElement('script');
          s2.src = props.playerComponentsUrl;
          s2.onload = () => {
            scriptLoaded.value = true;
            resolve();
          };
          s2.onerror = () => reject(new Error('aliplayer components load failed'));
          document.body.appendChild(s2);
        };
        s.onerror = () => reject(new Error('aliplayer load failed'));
        document.body.appendChild(s);
      });
    }

    // initialize player with videoData
    async function initPlayer() {
      showError.value = false;
      if (props.useWebViewForApp && props.platform !== 'web') {
        // In-app recommended to use WebView. Emit event so parent can take over.
        emit('error', { message: 'App environment required WebView. Set useWebViewForApp=false to attempt in-page' });
        showError.value = true;
        errorText.value = 'App environment recommended to use WebView for Aliplayer';
        return;
      }

      await loadAliplayer();

      // choose options
      const v = props.videoData || {};
      let options: Record<string, any> = {
        id: (playerContainer.value as HTMLElement).id || 'ali-player-' + Math.random().toString(36).slice(2),
        width: '100%',
        height: '100%',
        autoplay: props.autoplay,
        isLive: false,
        rePlay: false,
        playsinline: true,
        controlBarVisibility: 'hover',
        useH5Prism: true,
        // skinLayout can be extended if needed
        skinLayout: [
          { name: 'bigPlayButton', align: 'cc' },
          { name: 'H5Loading', align: 'cc' },
          { name: 'errorDisplay', align: 'tlabs' },
          { name: 'controlBar', align: 'blabs', children: [
            { name: 'progress', align: 'blabs' },
            { name: 'playButton', align: 'tl' },
            { name: 'timeDisplay', align: 'tl' },
            { name: 'prism-speed-selector', align: 'tr' },
            { name: 'volume', align: 'tr' }
          ] }
        ]
      };

      // decide source mode
      if (v.type === 1) {
        if (!v.m3u8Url) {
          // private encrypted: require vid+playAuth
          if (!v.videoId || !v.playAuth) {
            // parent might need to request playAuth
            emit('request-playauth', v);
            showError.value = true;
            errorText.value = '播放凭证缺失';
            return;
          }
          options = {
            ...options,
            vid: v.videoId,
            playauth: v.playAuth,
            encryptType: 1,
            playConfig: { EncryptType: 'AliyunVoDEncryption' }
          };
        } else {
          options = { ...options, source: v.m3u8Url };
        }
      } else {
        // not encrypted
        options = { ...options, source: v.videoUrl };
      }

      // add rate component by default
      options.components = [
        { name: 'RateComponent', type: (window as any).AliPlayerComponent?.RateComponent }
      ];

      // create player
      try {
        // ensure container has an id
        if (playerContainer.value && !(playerContainer.value as HTMLElement).id) {
          (playerContainer.value as HTMLElement).id = 'ali-player-' + Math.random().toString(36).slice(2);
        }
        const player = new (window as any).Aliplayer(options, function (p: any) {
          // ready
        });
        playerInstance.value = player;

        // event binds
        player.on('ready', () => {
          emit('ready');
          if (props.autoplay) player.play();
        });

        player.on('play', () => {
          emit('play');
        });

        player.on('pause', () => {
          pauseTime.value = Math.floor(player.getCurrentTime() || 0);
          emit('pause');
        });

        player.on('timeupdate', () => {
          const t = Math.floor(player.getCurrentTime() || 0);
          if (currentSeconds.value !== t) {
            currentSeconds.value = t;
            emit('timeupdate', { time: t, status: player.getStatus?.() });
            saveCounter.value++;
            // every autoSaveInterval seconds -> emit progress-save
            if (saveCounter.value >= props.autoSaveInterval) {
              saveCounter.value = 0;
              emit('progress-save', { videoId: props.videoData.id, position: currentSeconds.value });
              // also local save
              saveResumeItem(props.videoData.id, currentSeconds.value);
            }
          }
        });

        player.on('ended', () => {
          emit('ended', { videoId: props.videoData.id });
          // default behavior: start countdown then emit load-next
          startNextCountdown();
        });

        player.on('error', (e: any) => {
          showError.value = true;
          errorText.value = '播放出错';
          emit('error', e);
        });

        // seek to resume pos if present
        nextTick(() => {
          const list = loadResumeList();
          const idx = list.findIndex(item => item.id === props.videoData.id);
          const resumeTime = idx >= 0 ? list[idx].time : (props.videoData.firstTime || 0);
          const dur = player.getDuration ? Math.floor(player.getDuration() || 0) : 0;
          if (resumeTime && dur && resumeTime < dur) {
            player.seek(resumeTime);
          } else if (resumeTime && !dur) {
            // if duration unknown yet, attempt seek once canplay
            player.one && player.one('canplay', () => {
              const d2 = Math.floor(player.getDuration() || 0);
              if (resumeTime < d2) player.seek(resumeTime);
            });
          }
        });

        // periodic autosave fallback (in case events miss)
        if (autoSaveIntervalId.value) window.clearInterval(autoSaveIntervalId.value);
        autoSaveIntervalId.value = window.setInterval(() => {
          if (currentSeconds.value > 0) {
            emit('progress-save', { videoId: props.videoData.id, position: currentSeconds.value });
            saveResumeItem(props.videoData.id, currentSeconds.value);
          }
        }, props.autoSaveInterval * 1000);

      } catch (err) {
        showError.value = true;
        errorText.value = '播放器初始化失败';
        emit('error', err);
      }
    }

    // start next countdown
    function startNextCountdown(seconds = 5) {
      showCountDown.value = true;
      countDownSeconds.value = seconds;
      if (countdownTimerId.value) window.clearInterval(countdownTimerId.value);
      countdownTimerId.value = window.setInterval(() => {
        countDownSeconds.value -= 1;
        if (countDownSeconds.value <= 0) {
          // trigger parent to load next
          window.clearInterval(countdownTimerId.value!);
          showCountDown.value = false;
          emit('load-next', { videoId: props.videoData.id });
        }
      }, 1000);
    }

    function cancelNext() {
      showCountDown.value = false;
      if (countdownTimerId.value) window.clearInterval(countdownTimerId.value);
      emit('change-screen', { action: 'cancel-next' });
    }

    // control API exposed
    function play() {
      playerInstance.value && playerInstance.value.play && playerInstance.value.play();
    }
    function pause() {
      playerInstance.value && playerInstance.value.pause && playerInstance.value.pause();
    }
    function seek(sec: number) {
      playerInstance.value && playerInstance.value.seek && playerInstance.value.seek(sec);
    }
    function replay() {
      if (playerInstance.value) {
        playerInstance.value.seek(0);
        playerInstance.value.play();
      }
    }
    function enterFullscreen() {
      if (!playerInstance.value) return;
      const status = playerInstance.value.fullscreenService.getIsFullScreen && playerInstance.value.fullscreenService.getIsFullScreen();
      if (status) {
        playerInstance.value.fullscreenService.cancelFullScreen && playerInstance.value.fullscreenService.cancelFullScreen();
        emit('change-screen', { status: false });
        // example: lock portrait if in app (needs plus.* or native)
      } else {
        playerInstance.value.fullscreenService.requestFullScreen && playerInstance.value.fullscreenService.requestFullScreen();
        emit('change-screen', { status: true });
      }
    }

    // watch videoData changes
    watch(() => props.videoData, async (nv) => {
      // dispose old player
      if (playerInstance.value && playerInstance.value.dispose) {
        try { playerInstance.value.dispose(); } catch (e) { console.warn(e); }
        playerInstance.value = null;
      }
      // reset states
      currentSeconds.value = 0;
      pauseTime.value = 0;
      showError.value = false;
      // init new
      await initPlayer();
    }, { immediate: true, deep: true });

    onMounted(() => {
      // ensure container has unique id for Aliplayer
      if (playerContainer.value && !(playerContainer.value as HTMLElement).id) {
        (playerContainer.value as HTMLElement).id = 'ali-player-' + Math.random().toString(36).slice(2);
      }
    });

    onBeforeUnmount(() => {
      if (playerInstance.value && playerInstance.value.dispose) {
        try { playerInstance.value.dispose(); } catch (e) { /* ignore */ }
      }
      if (autoSaveIntervalId.value) window.clearInterval(autoSaveIntervalId.value);
      if (countdownTimerId.value) window.clearInterval(countdownTimerId.value);
    });

    // expose methods to parent via ref
    expose({
      play, pause, seek, replay, startNextCountdown
    });

    return {
      playerContainer,
      playerHeight,
      play, pause, seek, replay,
      showCountDown, countDownSeconds, cancelNext,
      showError, errorText
    };
  }
});
</script>

<style scoped>
.ali-player-wrapper { position: relative; width: 100%; }
.player-container { background: #000; }
.countdown-overlay {
  position: absolute; top: 0; right: 10px; z-index: 50;
  background: rgba(0,0,0,0.6); color: #fff; padding: 10px; border-radius: 6px;
}
.btn-cancel { margin-top: 8px; background: #fff; color: #000; border: none; padding:6px 12px; border-radius:4px; }
.player-error { color: #fff; text-align:center; padding: 20px; }
</style>
