<template>
	<view
		v-show="ts.visible && isStackTop"
		class="gnp-wrap"
		:class="{ 'gnp-wrap--touch': gnpTouching }"
		:style="gnpWrapDragStyle"
		@touchstart.stop="gnpOnTouchStart"
		@touchmove.stop="gnpOnTouchMove"
		@touchend.stop="gnpOnTouchEnd"
		@touchcancel.stop="gnpOnTouchCancel"
	>
		<view class="gnp-card" :class="{ 'gnp-card--in': ts.enterActive }">
			<view class="gnp-left">
				<text class="gnp-time">{{ ts.displayTime }}</text>
				<text class="gnp-text">{{ ts.displayText }}</text>
			</view>
			<view v-if="ts.displayPicture" class="gnp-icon-box">
				<image class="gnp-thumb" :src="ts.displayPicture" mode="aspectFill" />
			</view>
		</view>
	</view>
</template>

<script>
	import { noticeTopState, hideTopNotice } from '@/common/utils/noticeTopShared.js'

	const INDEX_ROUTE = 'pages/index/index'
	const UP_SWIPE = 50
	const TAP = 24
	const RE_STACK_MS = 300

	export default {
		name: 'AppTopPushNotice',
		data() {
			return {
				gnpTouchStartY: 0,
				gnpTouchStartX: 0,
				gnpDragOffset: 0,
				gnpTouching: false,
				_stackTick: 0,
				_stackTimer: null,
			}
		},
		computed: {
			ts() {
				// 依赖页面栈：非栈顶不展示本实例
				// eslint-disable-next-line no-unused-expressions
				this._stackTick
				// 显式读 observable 字段，否则子模板不随推送更新
				const {
					visible,
					enterActive,
					displayTime,
					displayText,
					displayPicture,
				} = noticeTopState
				return { visible, enterActive, displayTime, displayText, displayPicture }
			},
			isStackTop() {
				// eslint-disable-next-line no-unused-expressions
				this._stackTick
				try {
					const stack = getCurrentPages()
					if (!stack || !stack.length) return true
					const top = stack[stack.length - 1]
					return top && top.$vm === this.$root
				} catch (e) {
					return true
				}
			},
			gnpWrapDragStyle() {
				const o = this.gnpDragOffset
				if (o === 0) {
					return {}
				}
				return {
					transform: `translateY(${o}px)`,
					transition: this.gnpTouching ? 'none' : 'transform 0.2s ease-out',
				}
			},
		},
		watch: {
			'ts.visible'(v) {
				if (v) {
					this.gnpDragOffset = 0
					this.gnpTouching = false
				}
			},
		},
		mounted() {
			this._stackTimer = setInterval(() => {
				this._stackTick += 1
			}, RE_STACK_MS)
		},
		beforeDestroy() {
			if (this._stackTimer) {
				clearInterval(this._stackTimer)
				this._stackTimer = null
			}
		},
		methods: {
			gnpOnTouchStart(e) {
				if (!this.ts.visible || !this.isStackTop || !e.touches || !e.touches[0]) return
				this.gnpTouching = true
				this.gnpTouchStartY = e.touches[0].clientY
				this.gnpTouchStartX = e.touches[0].clientX
			},
			gnpOnTouchMove(e) {
				if (!this.ts.visible || !this.gnpTouching || !e.touches || !e.touches[0]) return
				const y = e.touches[0].clientY
				const dy = y - this.gnpTouchStartY
				if (dy < 0) {
					this.gnpDragOffset = Math.max(dy, -220)
				} else {
					this.gnpDragOffset = 0
				}
			},
			gnpOnTouchEnd(e) {
				if (!this.ts.visible) return
				const t = e.changedTouches && e.changedTouches[0]
				const dragAtEnd = this.gnpDragOffset
				this.gnpTouching = false
				this.gnpDragOffset = 0
				if (!t) return
				const y = t.clientY
				const x = t.clientX
				const totalDy = y - this.gnpTouchStartY
				const totalDx = x - this.gnpTouchStartX
				if (totalDy < -UP_SWIPE || dragAtEnd < -UP_SWIPE) {
					hideTopNotice()
					return
				}
				if (Math.abs(totalDy) <= TAP && Math.abs(totalDx) <= TAP) {
					hideTopNotice()
					this._openNotificationTab()
				}
			},
			gnpOnTouchCancel() {
				if (!this.ts.visible) return
				this.gnpTouching = false
				this.gnpDragOffset = 0
			},
			_openNotificationTab() {
				try {
					const stack = getCurrentPages()
					const cur = stack && stack.length ? stack[stack.length - 1] : null
					const route = cur && (cur.route || (cur.$page && cur.$page.fullPath))
					if (route && String(route).replace(/^\//, '') === INDEX_ROUTE) {
						uni.$emit('indexOpenNotificationTab')
						return
					}
				} catch (e) {}
				uni.reLaunch({ url: '/pages/index/index?tab=2' })
			},
		},
	}
</script>

<style scoped>
	.gnp-wrap {
		position: fixed;
		left: 0;
		right: 0;
		top: 100rpx;
		z-index: 10050;
		padding-left: 24rpx;
		padding-right: 24rpx;
		padding-bottom: 8rpx;
		box-sizing: border-box;
		touch-action: none;
	}
	.gnp-wrap--touch {
		z-index: 10051;
	}
	.gnp-card {
		display: flex;
		flex-direction: row;
		align-items: center;
		justify-content: space-between;
		background: #ffffff;
		border-radius: 24rpx;
		padding: 30rpx 24rpx;
		box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.1);
		transform: translateY(-120%);
		opacity: 0;
		transition: transform 0.32s ease-out, opacity 0.28s ease;
	}
	.gnp-card--in {
		transform: translateY(0);
		opacity: 1;
	}
	.gnp-left {
		flex: 1;
		min-width: 0;
		display: flex;
		flex-direction: column;
		padding-right: 20rpx;
	}
	.gnp-time {
		font-size: 24rpx;
		color: #f59a23;
		line-height: 1.3;
		margin-bottom: 8rpx;
	}
	.gnp-text {
		font-size: 28rpx;
		color: #333333;
		line-height: 1.45;
		word-break: break-all;
		display: -webkit-box;
		-webkit-box-orient: vertical;
		-webkit-line-clamp: 3;
		line-clamp: 3;
		overflow: hidden;
	}
	.gnp-icon-box {
		width: 100rpx;
		height: 100rpx;
		border-radius: 20rpx;
		background: #f2f2f2;
		flex-shrink: 0;
		display: flex;
		align-items: center;
		justify-content: center;
		overflow: hidden;
	}
	.gnp-thumb {
		width: 100%;
		height: 100%;
		display: block;
	}
</style>
