<template>
	<scroll-view
		class="find-scroll"
		scroll-y
		:style="{ height: scrollHeight + 'px' }"
		:show-scrollbar="false"
		:lower-threshold="120"
		:refresher-enabled="true"
		:refresher-triggered="refreshing"
		refresher-background="#eef3fb"
		refresher-default-style="black"
		@refresherrefresh="onRefresherRefresh"
		@refresherrestore="onRefresherEnd"
		@refresherabort="onRefresherEnd"
		@scrolltolower="onLower"
	>
		<view v-if="loading && !rows.length" class="find-loading">
			<view
				v-for="n in skeletonCount"
				:key="'sk-' + n"
				class="find-skeleton-card"
			>
				<view class="find-skel-hd">
					<view class="find-skel-avatar find-skel-shine" />
					<view class="find-skel-meta">
						<view class="find-skel-line find-skel-line--name find-skel-shine" />
						<view class="find-skel-line find-skel-line--sub find-skel-shine" />
					</view>
				</view>
				<view class="find-skel-line find-skel-line--text find-skel-shine" />
				<view class="find-skel-line find-skel-line--text find-skel-line--short find-skel-shine" />
				<view class="find-skel-media find-skel-shine" />
				<view class="find-skel-ft">
					<view
						v-for="i in 3"
						:key="'chip-' + i"
						class="find-skel-chip find-skel-shine"
					/>
				</view>
			</view>
			<view class="find-loading-hint">
				<view class="find-spinner find-spinner--lg" />
				<text class="find-loading-hint-text">{{ labels.loading }}</text>
			</view>
		</view>
		<view v-else-if="!rows.length" class="find-empty">
			<view class="find-empty-card">
				<view class="find-empty-icon-wrap">
					<text class="find-empty-icon">✦</text>
				</view>
				<text class="find-empty-title">{{ labels.empty }}</text>
				<text class="find-empty-desc">{{ labels.emptyHint }}</text>
			</view>
		</view>

		<view
			v-for="(item, index) in rows"
			:key="item.id + '_' + index"
			class="find-card"
		>
			<view class="find-card-hd">
				<view class="find-user" @tap="onUserTap(item)">
					<image class="find-avatar" :src="avatarOf(item)" mode="aspectFill" />
					<view class="find-user-meta">
						<text class="find-nick">{{ nicknameOf(item) }}</text>
						<view v-if="locationOf(item)" class="find-loc">
							<text class="find-loc-pin">📍</text>
							<text class="find-loc-text">{{ locationOf(item) }}</text>
						</view>
					</view>
				</view>
				<view class="find-bookmark" @tap.stop="onBookmark(item)">
					<text class="find-bookmark-icon">🔖</text>
				</view>
			</view>

			<!-- 正文摘要 -->
			<view v-if="contentPreview(item)" class="find-content" @tap="openItem(item)">
				<text v-if="item.type_text" class="find-tag">{{ item.type_text }}</text>
				<text class="find-content-text">{{ contentPreview(item) }}</text>
			</view>

			<!-- 视频 -->
			<view v-if="item.type === 'video'" class="find-media" @tap="openItem(item)">
				<view class="find-video-wrap">
					<image class="find-video-cover" :src="videoCoverOf(item)" mode="aspectFill" />
					<view class="find-play-badge">
						<text class="find-play-icon">▶</text>
					</view>
				</view>
			</view>

			<!-- 直播 -->
			<view v-else-if="item.type === 'live'" class="find-media" @tap="openItem(item)">
				<view class="find-live-wrap">
					<image class="find-live-cover" :src="liveCoverOf(item)" mode="aspectFill" />
					<view class="find-live-badge">
						<text class="find-live-text">{{ liveBadge(item) }}</text>
					</view>
				</view>
			</view>

			<!-- 多图九宫格 -->
			<view v-else-if="hasImages(item)" class="find-media">
				<post-image-grid
					:images="item.images"
					@tap-image="onImageTap(item, $event)"
				/>
			</view>

			<view class="find-card-ft">
				<view class="find-action" @tap.stop="onLike(index)">
					<text class="find-like-icon" :class="{ 'find-like-icon--on': item.isLike === 1 }">♥</text>
					<text class="find-action-num">{{ item.likes || 0 }}</text>
				</view>
				<view class="find-action" @tap.stop="openItem(item, 'comments')">
					<text class="find-comment-icon">💬</text>
					<text class="find-action-num">{{ item.comments || 0 }}</text>
				</view>
				<view class="find-action">
					<text class="find-view-icon">👁</text>
					<text class="find-action-num">{{ item.views || 0 }}</text>
				</view>
			</view>
		</view>

		<view v-if="rows.length" class="find-footer">
			<view v-if="loadingMore" class="find-footer-busy">
				<view class="find-spinner" />
				<text class="find-footer-text">{{ labels.loading }}</text>
			</view>
			<text v-else-if="footerText" class="find-footer-text">{{ footerText }}</text>
		</view>

		<view class="find-scroll-pad" :style="{ height: bottomInset + 'px' }" />
	</scroll-view>
</template>

<script>
import PostImageGrid from '@/components/dynamics/post-image-grid.vue'
import { stripHtml } from '@/features/feed/clip-normalize'
import { detailPageUrl, videoDetailUrl } from '@/features/dynamics/post-media'
import {
	fetchDiscoverFeedPage,
	toggleClipLike
} from '@/features/feed/feed-gateway'

export default {
	name: 'FindFeedList',
	components: { PostImageGrid },
	props: {
		scrollHeight: { type: Number, default: 500 },
		bottomInset: { type: Number, default: 0 },
		keyword: { type: String, default: '' },
		emptyText: { type: String, default: '' },
		loadingText: { type: String, default: '' },
		noMoreText: { type: String, default: '' },
		guestLabel: { type: String, default: '' },
		loadFailedText: { type: String, default: '' },
		actionFailedText: { type: String, default: '' },
		contentUnavailableText: { type: String, default: '' },
		userProfileDevText: { type: String, default: '' },
		bookmarkDevText: { type: String, default: '' },
		liveReplayText: { type: String, default: '' }
	},
	data() {
		return {
			rows: [],
			pageNo: 1,
			pageEnd: 1,
			loading: false,
			loadingMore: false,
			refreshing: false,
			status: 'more',
			skeletonCount: 3
		}
	},
	computed: {
		labels() {
			const t = this.$t.bind(this)
			return {
				loading: this.loadingText || t('dynamics.loading'),
				noMore: this.noMoreText || t('dynamics.noMore'),
				empty: this.emptyText || t('dynamics.emptyFeed'),
				emptyHint: t('dynamics.emptyFeedHint'),
				guest: this.guestLabel || t('dynamics.guest'),
				loadFailed: this.loadFailedText || t('dynamics.loadFailed'),
				actionFailed: this.actionFailedText || t('dynamics.actionFailed'),
				contentUnavailable: this.contentUnavailableText || t('dynamics.contentUnavailable'),
				userProfileDev: this.userProfileDevText || t('dynamics.userProfileDev'),
				bookmarkDev: this.bookmarkDevText || t('dynamics.bookmarkDev'),
				liveReplay: this.liveReplayText || t('dynamics.liveReplay')
			}
		},
		signedIn() {
			return !!(this.$store && this.$store.state && this.$store.state.user && this.$store.state.user.isLogin)
		},
		footerText() {
			if (this.loadingMore) return this.labels.loading
			if (this.status === 'noMore') return this.labels.noMore
			return ''
		}
	},
	watch: {
		keyword() {
			this.reload()
		}
	},
	mounted() {
		this.reload()
	},
	methods: {
		oss(url, w = 400, h = 0) {
			if (this.$appKit && typeof this.$appKit.oss === 'function') {
				return this.$appKit.oss(url, w, h)
			}
			return url || ''
		},
		nicknameOf(item) {
			if (item.shop && item.shop.shopname) return item.shop.shopname
			if (item.user && item.user.nickname) return item.user.nickname
			return item.username || this.labels.guest
		},
		avatarOf(item) {
			if (item.shop && item.shop.avatar) return this.oss(item.shop.avatar, 72, 72)
			if (item.user && item.user.avatar) return this.oss(item.user.avatar, 72, 72)
			return this.oss('', 72, 72)
		},
		locationOf(item) {
			return item.city || item.address || item.region || item.location || ''
		},
		hasImages(item) {
			return Array.isArray(item.images) && item.images.length > 0
		},
		contentPreview(item) {
			const text = stripHtml(item.content)
			if (!text) return ''
			return text.length > 120 ? `${text.slice(0, 120)}…` : text
		},
		videoCoverOf(item) {
			const cover = (item.video && item.video.cover_url) || (item.images && item.images[0]) || ''
			return this.oss(cover, 750, 0)
		},
		liveCoverOf(item) {
			const cover = (item.images && item.images[0]) || ''
			return this.oss(cover, 750, 0)
		},
		liveBadge(item) {
			if (item.live && String(item.live.state) === '1') return 'LIVE'
			return this.labels.liveReplay
		},
		async reload(options = {}) {
			const keepRows = !!(options && options.keepRows)
			this.pageNo = 1
			this.pageEnd = 1
			if (!keepRows) this.rows = []
			this.status = 'more'
			await this.loadPage('refresh', { keepRows })
		},
		/** 下拉刷新：保留当前列表，避免闪屏 */
		async pullRefresh() {
			if (this.refreshing) return
			this.refreshing = true
			try {
				await this.reload({ keepRows: true })
			} finally {
				this.refreshing = false
				try {
					uni.stopPullDownRefresh()
				} catch (_) {}
			}
		},
		onRefresherRefresh() {
			this.pullRefresh()
		},
		onRefresherEnd() {
			this.refreshing = false
		},
		onLower() {
			if (this.loading || this.loadingMore || this.status === 'noMore') return
			this.loadPage('lower')
		},
		async loadPage(mode, options = {}) {
			const keepRows = options.keepRows === true
			if (mode === 'lower') {
				if (this.pageNo >= this.pageEnd) {
					this.status = 'noMore'
					return
				}
				this.loadingMore = true
			} else if (!keepRows) {
				this.loading = true
			}

			const nextPage = mode === 'lower' ? this.pageNo + 1 : 1
			const filters = {}
			if (this.keyword && String(this.keyword).trim()) {
				filters.search = String(this.keyword).trim()
			}
			try {
				const pack = await fetchDiscoverFeedPage(nextPage, filters)
				const list = pack.rows || []
				this.pageNo = pack.currentPage || nextPage
				this.pageEnd = pack.lastPage || this.pageNo
				this.rows = mode === 'lower' ? this.rows.concat(list) : list
				this.status = this.pageNo >= this.pageEnd || !list.length ? 'noMore' : 'more'
			} catch (err) {
				if (!this.rows.length) this.status = 'noMore'
				this.$appKit && this.$appKit.msg(err.message || this.labels.loadFailed)
			} finally {
				this.loading = false
				this.loadingMore = false
				if (mode === 'refresh') {
					try { uni.stopPullDownRefresh() } catch (_) {}
				}
			}
		},
		onUserTap(item) {
			if (!item) return
			const userNo =
				item.user_no ||
				(item.user && (item.user.user_no || item.user.id))
			if (!userNo) return
			uni.navigateTo({
				url: `/pages/profile/show?id=${encodeURIComponent(String(userNo))}`
			})
		},
		onBookmark(item) {
			// this.$appKit && this.$appKit.msg(this.labels.bookmarkDev)
		},
		async onLike(index) {
			if (!this.$appKit || !this.$appKit.requireLogin()) return
			const row = this.rows[index]
			if (!row || !row.id) return
			try {
				const flag = await toggleClipLike(row.id)
				if (flag === 0 || flag === '0') {
					row.isLike = 0
					row.likes = Math.max(0, Number(row.likes || 0) - 1)
				} else if (flag === 1 || flag === '1') {
					row.isLike = 1
					row.likes = Number(row.likes || 0) + 1
				}
			} catch (err) {
				this.$appKit && this.$appKit.msg(err.message || this.labels.actionFailed)
			}
		},
		onImageTap(item, imgIndex) {
			this.openItem(item, '', imgIndex)
		},
		openItem(item, scene, imgIndex = 0) {
			if (item.state && item.state !== 'normal') {
				this.$appKit && this.$appKit.msg(this.labels.contentUnavailable)
				return
			}
			if (item.type === 'live' && item.live_id) {
				this.$appKit && this.$appKit.auth(`/pages/live/watch?id=${item.live_id}`)
				return
			}
			if (item.type === 'video') {
				uni.navigateTo({
					url: videoDetailUrl(item.id, { labelId: item.label_id })
				})
				return
			}
			const url = detailPageUrl(item.id, {
				focus: scene === 'comments' ? 'comments' : '',
				img: imgIndex
			})
			uni.navigateTo({ url })
		}
	}
}
</script>

<style scoped>
.find-scroll {
	width: 100%;
}
.find-loading {
	padding: 8rpx 0 24rpx;
}

.find-skeleton-card {
	margin: 0 32rpx 28rpx;
	padding: 28rpx;
	background-color: #ffffff;
	border-radius: 28rpx;
	box-shadow: 0 8rpx 32rpx rgba(74, 140, 255, 0.08);
}

.find-skel-hd {
	display: flex;
	flex-direction: row;
	align-items: center;
	margin-bottom: 24rpx;
}

.find-skel-avatar {
	width: 72rpx;
	height: 72rpx;
	border-radius: 36rpx;
	flex-shrink: 0;
}

.find-skel-meta {
	flex: 1;
	margin-left: 20rpx;
}

.find-skel-line {
	height: 24rpx;
	border-radius: 12rpx;
	margin-bottom: 12rpx;
}

.find-skel-line--name {
	width: 42%;
	height: 28rpx;
}

.find-skel-line--sub {
	width: 28%;
	margin-bottom: 0;
}

.find-skel-line--text {
	width: 100%;
}

.find-skel-line--short {
	width: 72%;
	margin-bottom: 20rpx;
}

.find-skel-media {
	width: 100%;
	height: 320rpx;
	border-radius: 20rpx;
	margin-bottom: 24rpx;
}

.find-skel-ft {
	display: flex;
	flex-direction: row;
	align-items: center;
}

.find-skel-chip {
	width: 88rpx;
	height: 28rpx;
	border-radius: 14rpx;
	margin-right: 24rpx;
}

.find-skel-shine {
	background: linear-gradient(
		90deg,
		#e8edf5 0%,
		#f4f7fc 45%,
		#e8edf5 90%
	);
	background-size: 200% 100%;
	animation: find-skel-shine 1.4s ease-in-out infinite;
}

@keyframes find-skel-shine {
	0% {
		background-position: 100% 0;
	}
	100% {
		background-position: -100% 0;
	}
}

.find-loading-hint {
	display: flex;
	flex-direction: row;
	align-items: center;
	justify-content: center;
	padding: 8rpx 0 32rpx;
}

.find-loading-hint-text {
	margin-left: 16rpx;
	font-size: 26rpx;
	color: #8a8d9e;
	letter-spacing: 1rpx;
}

.find-empty {
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 48rpx 32rpx 80rpx;
	min-height: 360rpx;
	box-sizing: border-box;
}

.find-empty-card {
	width: 100%;
	padding: 56rpx 40rpx;
	background-color: #ffffff;
	border-radius: 28rpx;
	box-shadow: 0 8rpx 32rpx rgba(74, 140, 255, 0.08);
	display: flex;
	flex-direction: column;
	align-items: center;
}

.find-empty-icon-wrap {
	width: 112rpx;
	height: 112rpx;
	border-radius: 56rpx;
	background: linear-gradient(135deg, rgba(74, 140, 255, 0.14) 0%, rgba(110, 200, 255, 0.08) 100%);
	display: flex;
	align-items: center;
	justify-content: center;
	margin-bottom: 28rpx;
}

.find-empty-icon {
	font-size: 48rpx;
	color: #4a8cff;
	font-weight: 700;
}

.find-empty-title {
	font-size: 30rpx;
	font-weight: 700;
	color: #1a1d26;
	text-align: center;
}

.find-empty-desc {
	margin-top: 12rpx;
	font-size: 26rpx;
	color: #8a8d9e;
	text-align: center;
	line-height: 1.5;
}

.find-spinner {
	width: 28rpx;
	height: 28rpx;
	border: 4rpx solid rgba(74, 140, 255, 0.18);
	border-top-color: #4a8cff;
	border-radius: 50%;
	animation: find-spin 0.75s linear infinite;
	flex-shrink: 0;
}

.find-spinner--lg {
	width: 36rpx;
	height: 36rpx;
	border-width: 5rpx;
}

@keyframes find-spin {
	to {
		transform: rotate(360deg);
	}
}

.find-footer-busy {
	display: flex;
	flex-direction: row;
	align-items: center;
	justify-content: center;
	padding: 8rpx 0;
}
.find-card {
	margin: 0 32rpx 28rpx;
	padding: 28rpx;
	background-color: #ffffff;
	border-radius: 28rpx;
	box-shadow: 0 8rpx 32rpx rgba(74, 140, 255, 0.08);
}
.find-card-hd {
	display: flex;
	flex-direction: row;
	align-items: flex-start;
	justify-content: space-between;
	margin-bottom: 20rpx;
}
.find-user {
	display: flex;
	flex-direction: row;
	align-items: center;
	flex: 1;
	min-width: 0;
}
.find-avatar {
	width: 72rpx;
	height: 72rpx;
	border-radius: 36rpx;
	background-color: #eef2f8;
	flex-shrink: 0;
}
.find-user-meta {
	margin-left: 20rpx;
	flex: 1;
	min-width: 0;
}
.find-nick {
	font-size: 30rpx;
	font-weight: 700;
	color: #1a1d26;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.find-loc {
	display: flex;
	flex-direction: row;
	align-items: center;
	margin-top: 6rpx;
}
.find-loc-pin {
	font-size: 22rpx;
	margin-right: 6rpx;
}
.find-loc-text {
	font-size: 24rpx;
	color: #8a8d9e;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.find-bookmark {
	width: 64rpx;
	height: 64rpx;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
}
.find-bookmark-icon {
	font-size: 36rpx;
	opacity: 0.55;
}
.find-content {
	margin-bottom: 20rpx;
}
.find-tag {
	display: inline-block;
	font-size: 22rpx;
	color: #4a8cff;
	background-color: rgba(74, 140, 255, 0.12);
	padding: 4rpx 12rpx;
	border-radius: 8rpx;
	margin-right: 8rpx;
}
.find-content-text {
	font-size: 28rpx;
	line-height: 42rpx;
	color: #3d4250;
}
.find-media {
	margin-bottom: 4rpx;
}
.find-video-wrap,
.find-live-wrap {
	position: relative;
	width: 100%;
	height: 420rpx;
	border-radius: 20rpx;
	overflow: hidden;
	background-color: #eef2f8;
}
.find-video-cover,
.find-live-cover {
	width: 100%;
	height: 100%;
}
.find-play-badge,
.find-live-badge {
	position: absolute;
	left: 50%;
	top: 50%;
	transform: translate(-50%, -50%);
}
.find-play-badge {
	width: 88rpx;
	height: 88rpx;
	border-radius: 44rpx;
	background-color: rgba(0, 0, 0, 0.35);
	display: flex;
	align-items: center;
	justify-content: center;
}
.find-play-icon {
	color: #ffffff;
	font-size: 32rpx;
	margin-left: 6rpx;
}
.find-live-badge {
	padding: 0 24rpx;
	height: 56rpx;
	border-radius: 999rpx;
	background-color: rgba(255, 77, 79, 0.88);
	display: flex;
	align-items: center;
	justify-content: center;
}
.find-live-text {
	color: #ffffff;
	font-size: 24rpx;
	font-weight: 700;
}
.find-card-ft {
	display: flex;
	flex-direction: row;
	align-items: center;
	margin-top: 24rpx;
}
.find-action {
	display: flex;
	flex-direction: row;
	align-items: center;
	margin-right: 36rpx;
}
.find-like-icon,
.find-comment-icon,
.find-view-icon {
	font-size: 30rpx;
	color: #b0b4c0;
	margin-right: 8rpx;
}
.find-like-icon--on {
	color: #ff4d4f;
}
.find-action-num {
	font-size: 26rpx;
	color: #5c6070;
}
.find-footer {
	padding: 16rpx 0 8rpx;
	display: flex;
	align-items: center;
	justify-content: center;
}

.find-footer-busy .find-footer-text {
	margin-left: 12rpx;
}

.find-footer-text {
	font-size: 24rpx;
	color: #b0b4c0;
	text-align: center;
}
</style>
