<template>
	<!-- H5 专用：AliPlayer 包装（prism-host.vue 根节点是真正的 <div class="prism-player">） -->
	<view class="player-wrap" :style="frameStyle">
		<prism-host
			v-if="hasSource"
			:key="hostKey"
			:autoplay="true"
			:playsinline="true"
			:source="source"
			:height="hostHeight"
			:videoHeight="hostHeight"
			:width="hostWidth"
			videoWidth="auto"
			:skinLayout="false"
			:preload="true"
			:isLive="true"
			:format="activeFormat"
			controlBarVisibility="never"
			:enableStashBufferForFlv="true"
			:stashInitialSizeForFlv="4"
			:loadDataTimeout="10"
			:waitingTimeout="30"
			:liveRetry="5"
			@liveStreamStop="onLiveStop"
			@error="onAliError"
			@play="onAliPlay"
			@ready="onAliReady"
		/>
	</view>
</template>

<script>
import PrismHost from './prism-host.vue'

function pickAliFormat(url) {
	const lower = String(url || '').toLowerCase().split('?')[0]
	if (lower.endsWith('.flv')) return 'flv'
	if (lower.endsWith('.mp4')) return 'mp4'
	if (lower.endsWith('.m3u8')) return 'm3u8'
	return 'm3u8'
}

const H5_MAX_RETRY = 1
const H5_RETRY_DELAY_MS = 2000
// AliPlayer 错误码：4008 缓冲超时 / 4001 网络异常 / 4002 网络断开 /
// 4006 加载失败 / 4007 网络较差 / 4009 视频加载失败
// → 通常是 ts 加载失败（直播流刚开播 ts 不足、瞬时抖动、服务器问题）。
// 给 1 次重试机会兜底；若稳定失败说明是服务器侧问题，前端无法绕过。
// 4011 移动端不支持 flv/rtmp / 4003 视频解码失败 → 切流也无效，直接报错给上层。
const H5_RETRY_CODES = new Set([4001, 4002, 4006, 4007, 4008, 4009])

/**
 * 调试用：主动抓一次 m3u8 内容，把里面 ts 路径打出来。
 * 失败说明 m3u8 本身就 CORS / 鉴权问题；成功但 ts 404 说明推流断了。
 */
function probeM3u8(url) {
	if (!url) return
	try {
		console.log('[STREAM-PLAYER:H5] probing m3u8 ...', url)
		fetch(url, { credentials: 'omit', cache: 'no-store' })
			.then(async (r) => {
				const headers = {}
				try { r.headers.forEach((v, k) => { headers[k] = v }) } catch (_) {}
				const text = await r.text().catch(() => '')
				console.log('[STREAM-PLAYER:H5] m3u8 probe result', {
					status: r.status,
					ok: r.ok,
					cors_origin: headers['access-control-allow-origin'] || '<missing>',
					content_type: headers['content-type'] || '<missing>',
					body_len: text.length,
					body_head: text.slice(0, 600)
				})
				const tsLines = text.split(/\r?\n/).filter((s) => s && !s.startsWith('#'))
				console.log('[STREAM-PLAYER:H5] m3u8 ts entries:', tsLines.length, tsLines)
				if (tsLines.length > 0) {
					const first = tsLines[0]
					const tsUrl = /^https?:\/\//i.test(first)
						? first
						: url.replace(/[^/]*$/, '') + first
					console.log('[STREAM-PLAYER:H5] probing first ts ...', tsUrl)
					fetch(tsUrl, { credentials: 'omit', cache: 'no-store', method: 'GET' })
						.then((rr) => {
							const hh = {}
							try { rr.headers.forEach((v, k) => { hh[k] = v }) } catch (_) {}
							console.log('[STREAM-PLAYER:H5] ts probe result', {
								status: rr.status,
								ok: rr.ok,
								cors_origin: hh['access-control-allow-origin'] || '<missing>',
								content_type: hh['content-type'] || '<missing>',
								content_length: hh['content-length'] || '<missing>'
							})
						})
						.catch((e) => console.error('[STREAM-PLAYER:H5] ts probe FAIL', e && e.message))
				}
			})
			.catch((e) => console.error('[STREAM-PLAYER:H5] m3u8 probe FAIL', e && e.message))
	} catch (e) {
		console.error('[STREAM-PLAYER:H5] probe threw', e && e.message)
	}
}

export default {
	name: 'StreamPlayerH5',
	components: { PrismHost },
	props: {
		source: { type: String, default: '' },
		frameWidth: { type: Number, default: 0 },
		frameHeight: { type: Number, default: 0 },
		phase: { type: String, default: 'onair' }
	},
	data() {
		return {
			hostKey: 0,
			h5RetryCount: 0,
			h5RetryTimer: null
		}
	},
	computed: {
		frameStyle() {
			const w = this.frameWidth ? `${this.frameWidth}px` : '100%'
			const h = this.frameHeight ? `${this.frameHeight}px` : '100%'
			return { width: w, height: h }
		},
		hasSource() {
			return !!this.source
		},
		hostWidth() {
			return this.frameWidth ? `${this.frameWidth}px` : '100%'
		},
		hostHeight() {
			return this.frameHeight ? `${this.frameHeight}px` : '100%'
		},
		activeFormat() {
			return pickAliFormat(this.source)
		}
	},
	watch: {
		source: {
			immediate: true,
			handler(next) {
				this.cancelH5Retry()
				this.h5RetryCount = 0
				this._probeDone = false
				this.hostKey += 1
				console.log('[STREAM-PLAYER:H5] source set to', next)
			}
		}
	},
	mounted() {
		console.log('=== [STREAM-PLAYER:H5] mounted ===', {
			source: this.source,
			phase: this.phase,
			frameWidth: this.frameWidth,
			frameHeight: this.frameHeight
		})
	},
	beforeDestroy() {
		console.log('=== [STREAM-PLAYER:H5] beforeDestroy ===')
		this.cancelH5Retry()
	},
	methods: {
		onAliReady() {
			console.log('[STREAM-PLAYER:H5] AliPlayer ready @', this.source)
		},
		onAliPlay() {
			console.log('[STREAM-PLAYER:H5] AliPlayer play @', this.source, ' (retry =', this.h5RetryCount, ')')
			this.h5RetryCount = 0
			this.cancelH5Retry()
		},
		onLiveStop() {
			console.log('[STREAM-PLAYER:H5] liveStreamStop bubbled up')
			this.$emit('liveStop')
		},
		onAliError(e) {
			const code = Number(e && e.paramData && e.paramData.error_code)
			const msg = e && e.paramData && e.paramData.error_msg
			console.error('[STREAM-PLAYER:H5] AliPlayer ERROR code=' + code + ' msg=' + msg + ' src=' + this.source + ' retry=' + this.h5RetryCount)

			if (!this._probeDone) {
				this._probeDone = true
				probeM3u8(this.source)
			}

			if (H5_RETRY_CODES.has(code) && this.h5RetryCount < H5_MAX_RETRY) {
				this.h5RetryCount += 1
				console.warn('[STREAM-PLAYER:H5] scheduling m3u8 retry #' + this.h5RetryCount + ' in ' + H5_RETRY_DELAY_MS + 'ms')
				this.cancelH5Retry()
				this.h5RetryTimer = setTimeout(() => {
					this.h5RetryTimer = null
					console.warn('[STREAM-PLAYER:H5] retry #' + this.h5RetryCount + ' remount AliPlayer')
					this.hostKey += 1
				}, H5_RETRY_DELAY_MS)
				return
			}

			console.error('[STREAM-PLAYER:H5] giving up after ' + this.h5RetryCount + ' retries, error bubbles to parent')
			this.$emit('error', e)
		},
		cancelH5Retry() {
			if (this.h5RetryTimer) {
				clearTimeout(this.h5RetryTimer)
				this.h5RetryTimer = null
			}
		}
	}
}
</script>

<style>
.player-wrap {
	position: absolute;
	top: 0;
	left: 0;
	overflow: hidden;
	background-color: #000000;
}
</style>
