<template>
	<view class="app-update-page">
		<app-top-push-notice />
		<view class="tabback">
			<view class="rtjt" @tap="goBack">←</view>
			<view class="name">{{ $i18n.t('softwareUpdate') }}</view>
			<view class="tabback-placeholder" />
		</view>
		<view class="tabback-spacer" />

		<scroll-view class="main-scroll" scroll-y :show-scrollbar="false">
			<view class="main-pad">
				<view class="panel">
					<view class="ios-head">
						<view class="ios-icon-wrap">
							<image
								class="ios-icon"
								src="/static/newlogo.png"
								mode="aspectFill"
							/>
						</view>
						<view class="ios-head-main">
							<text class="ios-title">{{ headlineTitle }}</text>
							<text class="ios-subtitle">{{ headlineSubtitle }}</text>
						</view>
					</view>

					<rich-text
						v-if="updateContentHtml"
						class="ios-body-rich"
						:nodes="updateContentHtml"
					/>
					<text v-else class="ios-body preline">{{ mainBodyText }}</text>
				</view>
				<view class="scroll-tail" />
			</view>
		</scroll-view>

		<view class="footer-bar">
			<text class="footer-version">
				{{ $i18n.t('appUpdateCurrentVersionLabel') }}
				<text class="footer-version-num">{{ currentVersionDisplay }}</text>
			</text>
			<view
				class="footer-btn"
				:class="{ 'footer-btn--disabled': footerDisabled }"
				@tap="onFooterClick"
			>
				<text class="footer-btn-text">{{ footerBtnText }}</text>
			</view>
		</view>

		<!-- 下载进度（真实进度，完成后直接调起安装，无「完成」提示） -->
		<view v-if="updateProgressVisible" class="progress-mask" @tap.stop="">
			<view class="progress-card" @tap.stop="">
				<view class="progress-card-glow" />
				<view class="progress-top">
					<view class="progress-pct-huge">
						<text class="pct-value">{{ updateProgress }}</text>
						<text class="pct-unit">%</text>
					</view>
					<text class="progress-headline">{{ $i18n.t('appUpdateDownloading') }}</text>
					<text class="progress-stage">{{ $i18n.t('appUpdateStageDownload') }}</text>
				</view>

				<view class="progress-track-shell">
					<view class="progress-track-bg">
						<view class="progress-fill" :style="{ width: progressWidth }">
							<view class="progress-shimmer" />
							<view class="progress-fill-head" />
						</view>
					</view>
				</view>

				<text class="progress-hint">{{ $i18n.t('appUpdateInProgress') }}</text>
			</view>
		</view>
	</view>
</template>

<script>
	import {
		getCurrentAppVersionInfo,
		pickPackageSizeLabel,
		extractLatestApkRecord,
		normalizeVersionString,
		pickDownloadUrl
	} from '@/common/utils/appVersion.js'
	import {
		readGlobalVersionFromStorage,
		syncGlobalVersionToLocalStorage
	} from '@/common/config/appVersionConfig.js'

	export default {
		name: 'AppUpdatePage',
		data() {
			return {
				hasUpdate: false,
				currentVersion: '',
				newVersionName: '',
				newVersionIntro: '',
				updateContentHtml: '',
				downloadUrl: '',
				packageSizeLabel: '',
				updateProgressVisible: false,
				updateProgress: 0,
				updateRunning: false,
				_downloadTask: null
			}
		},
		computed: {
			progressWidth() {
				return `${Math.min(100, Math.max(0, this.updateProgress))}%`
			},
			headlineTitle() {
				const brand = this.$i18n.t('appBrandName')
				if (this.hasUpdate && this.newVersionName) {
					return `${brand} ${this.newVersionName}`
				}
				return `${brand} ${this.currentVersion}`
			},
			headlineSubtitle() {
				if (this.hasUpdate) {
					return (
						this.packageSizeLabel ||
						this.$i18n.t('appUpdatePackageSizeFallback')
					)
				}
				return this.$i18n.t('appUpdateAlreadyLatest')
			},
			mainBodyText() {
				if (this.updateContentHtml) {
					return ''
				}
				if (this.hasUpdate) {
					return (
						this.newVersionIntro ||
						this.$i18n.t('appUpdateDefaultNewIntro')
					)
				}
				return (
					this.$i18n.t('appUpdateHeroLatestHint') +
					'\n\n' +
					this.$i18n.t('appUpdateCurrentOptimizations') +
					'\n' +
					this.$i18n.t('appUpdateCurrentBuildNotes')
				)
			},
			footerDisabled() {
				return !this.hasUpdate || this.updateRunning
			},
			footerBtnText() {
				if (this.updateRunning) return this.$i18n.t('appUpdateInProgress')
				return this.hasUpdate
					? this.$i18n.t('appUpdateNow')
					: this.$i18n.t('appUpdateBtnAlreadyUpdated')
			},
			/** 底部展示的当前版本（与接口对比用的本地版本一致） */
			currentVersionDisplay() {
				const v = (this.currentVersion || '').trim()
				return v || '—'
			}
		},
		onShow() {
			this.refreshVersionState()
		},
		mounted() {
			uni.$on('languageChanged', this.onLang)
		},
		beforeDestroy() {
			uni.$off('languageChanged', this.onLang)
			this.abortActiveDownload()
		},
		methods: {
			onLang() {
				this.$forceUpdate()
			},
			goBack() {
				if (this.updateRunning) return
				uni.navigateBack()
			},
			applyPendingSource(pending) {
				if (!pending || typeof pending !== 'object') return
				const size = pickPackageSizeLabel(pending)
				this.packageSizeLabel = size || this.$i18n.t('appUpdatePackageSizeFallback')
				this.newVersionName =
					pending.versionName != null && pending.versionName !== ''
						? String(pending.versionName)
						: pending.versionCode != null && Number(pending.versionCode) > 0
							? String(pending.versionCode)
							: ''
				const intro =
					pending.changelog != null && String(pending.changelog).trim() !== ''
						? String(pending.changelog).trim()
						: this.$i18n.t('appUpdateDefaultNewIntro')
				this.newVersionIntro = intro
				this.updateContentHtml =
					pending.updateContentHtml != null &&
					String(pending.updateContentHtml).trim() !== ''
						? String(pending.updateContentHtml).trim()
						: ''
				this.downloadUrl = pickDownloadUrl(pending)
			},
			clearNoUpdateUi() {
				this.hasUpdate = false
				this.newVersionName = ''
				this.newVersionIntro = ''
				this.updateContentHtml = ''
				this.downloadUrl = ''
				this.packageSizeLabel = ''
			},
			/** 请求最新包信息：接口 `version` 与当前安装包版本一致则已是最新，否则可更新 */
			async refreshVersionState() {
				// 对比必须以「当前运行包」为准（plus.runtime / 小程序线上版），不能用本地缓存优先：
				// 否则装新 APK 后若 APP_GLOBAL_VERSION_INFO 仍为旧值，会一直误判可更新。
				syncGlobalVersionToLocalStorage()
				const runtimeInfo = getCurrentAppVersionInfo()
				const localVer = normalizeVersionString(runtimeInfo.versionName)
				const storedFallback = normalizeVersionString(readGlobalVersionFromStorage() || '')
				this.currentVersion = localVer || storedFallback
				try {
					const res = await this.$http.get('/app/apkVersion/latest')
					if (!res || Number(res.code) !== 200) {
						this.clearNoUpdateUi()
						return
					}
					const record = extractLatestApkRecord(res)
					if (!record || !record.versionName) {
						this.clearNoUpdateUi()
						return
					}
					const remoteVer = normalizeVersionString(record.versionName)
					if (remoteVer === localVer) {
						this.hasUpdate = false
						this.newVersionName = ''
						this.newVersionIntro = ''
						this.downloadUrl = ''
						this.packageSizeLabel = pickPackageSizeLabel(record) || ''
						const html =
							record.updateContentHtml != null &&
							String(record.updateContentHtml).trim() !== ''
								? String(record.updateContentHtml).trim()
								: ''
						this.updateContentHtml = html
						return
					}
					this.hasUpdate = true
					this.applyPendingSource(record)
				} catch (e) {
					console.warn('[app-update] apkVersion/latest', e)
					this.clearNoUpdateUi()
				}
			},
			abortActiveDownload() {
				if (this._downloadTask && typeof this._downloadTask.abort === 'function') {
					try {
						this._downloadTask.abort()
					} catch (e) {}
				}
				this._downloadTask = null
			},
			_finishDownloadFailed() {
				this.updateProgressVisible = false
				this.updateRunning = false
				this.updateProgress = 0
				this._downloadTask = null
				uni.showToast({
					title: this.$i18n.t('appUpdateDownloadFailed'),
					icon: 'none',
					duration: 2500
				})
			},
			/** Android App：真实下载进度；完成后直接调起安装，无完成 Toast */
			startAndroidApkDownload(url) {
				this.abortActiveDownload()
				this.updateRunning = true
				this.updateProgressVisible = true
				this.updateProgress = 0
				const task = uni.downloadFile({
					url,
					success: (res) => {
						this._downloadTask = null
						if (res.statusCode !== 200 || !res.tempFilePath) {
							this._finishDownloadFailed()
							return
						}
						this.updateProgress = 100
						this.updateProgressVisible = false
						this.updateRunning = false
						// #ifdef APP-PLUS
						try {
							plus.runtime.install(
								res.tempFilePath,
								{},
								() => {},
								(e) => {
									const msg =
										e && e.message
											? e.message
											: this.$i18n.t('appUpdateInstallFailed')
									uni.showToast({ title: msg, icon: 'none', duration: 3000 })
								}
							)
						} catch (err) {
							uni.showToast({
								title: this.$i18n.t('appUpdateInstallFailed'),
								icon: 'none',
								duration: 2500
							})
						}
						// #endif
					},
					fail: () => {
						this._finishDownloadFailed()
					}
				})
				this._downloadTask = task
				if (task && typeof task.onProgressUpdate === 'function') {
					task.onProgressUpdate((r) => {
						const p =
							typeof r.progress === 'number'
								? r.progress
								: r.totalBytesExpectedToWrite > 0
									? Math.round(
											(100 * r.totalBytesWritten) /
												r.totalBytesExpectedToWrite
										)
									: 0
						this.updateProgress = Math.min(100, Math.max(0, Math.round(p)))
					})
				}
			},
			startUpdateFlow() {
				if (this.updateRunning) return
				const url = (this.downloadUrl || '').trim()
				if (!url) {
					uni.showToast({
						title: this.$i18n.t('appUpdateNoDownloadUrl'),
						icon: 'none'
					})
					return
				}
				// #ifdef APP-PLUS
				try {
					if (uni.getSystemInfoSync().platform === 'android') {
						this.startAndroidApkDownload(url)
						return
					}
				} catch (e) {}
				// #endif
				this.openExternal(url)
			},
			openExternal(url) {
				const u = String(url).trim()
				if (!u) return
				// #ifdef APP-PLUS
				try {
					plus.runtime.openURL(u)
				} catch (e) {
					uni.showToast({ title: this.$i18n.t('appUpdateNoDownloadUrl'), icon: 'none' })
				}
				// #endif
				// #ifdef H5
				try {
					if (typeof window !== 'undefined') window.open(u, '_blank')
				} catch (e) {
					uni.showToast({ title: this.$i18n.t('appUpdateNoDownloadUrl'), icon: 'none' })
				}
				// #endif
				// #ifdef MP-WEIXIN
				uni.setClipboardData({
					data: u,
					success: () => {
						uni.showToast({
							title: this.$i18n.t('appUpdateLinkCopied'),
							icon: 'none'
						})
					}
				})
				// #endif
			},
			onFooterClick() {
				if (!this.hasUpdate || this.updateRunning) return
				this.startUpdateFlow()
			}
		}
	}
</script>

<style scoped lang="scss">
	/* 黑白灰 + 主题色 #3996FD：字阶与对比统一 */
	$theme: #3996fd;
	$ink: #0a0a0a;
	$ink-strong: #141414;
	$ink-body: #3a3a3a;
	$ink-muted: #6b6b6b;
	$ink-sub: #8c8c8c;
	$ink-hint: #a3a3a3;
	$page-bg: #f0f0f0;
	$surface: #ffffff;
	$line: #e8e8e8;

	.app-update-page {
		min-height: 100vh;
		background-color: $page-bg;
		display: flex;
		flex-direction: column;
		box-sizing: border-box;
	}

	.tabback {
		width: 750rpx;
		height: 204rpx;
		background: $surface;
		position: fixed;
		top: 0;
		left: 0;
		display: flex;
		align-items: center;
		justify-content: space-between;
		padding: 0 28rpx;
		padding-top: 52rpx;
		box-sizing: border-box;
		border-bottom: 1px solid $line;
		z-index: 999;

		.name {
			font-size: 34rpx;
			color: $ink-strong;
			font-weight: 600;
			letter-spacing: -0.5rpx;
		}

		.rtjt {
			font-size: 38rpx;
			color: $ink-strong;
			font-weight: 400;
		}

		.tabback-placeholder {
			width: 36rpx;
			height: 36rpx;
		}
	}

	.tabback-spacer {
		width: 100%;
		height: 204rpx;
		flex-shrink: 0;
	}

	.main-scroll {
		flex: 1;
		height: 0;
		min-height: 0;
		margin-top: 30rpx;
	}

	.main-pad {
		padding: 0 32rpx;
		box-sizing: border-box;
	}

	.panel {
		background: $surface;
		border-radius: 16rpx;
		padding: 32rpx 28rpx 36rpx;
		box-sizing: border-box;
		border: 1rpx solid $line;
		box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.04);
	}

	.scroll-tail {
		height: calc(200rpx + env(safe-area-inset-bottom));
	}

	.ios-head {
		display: flex;
		flex-direction: row;
		align-items: flex-start;
	}

	.ios-icon-wrap {
		width: 100rpx;
		height: 100rpx;
		border-radius: 28rpx;
		overflow: hidden;
		flex-shrink: 0;
		background: #ebebeb;
		border: 1rpx solid $line;
	}

	.ios-icon {
		width: 100%;
		height: 100%;
		display: block;
	}

	.ios-head-main {
		flex: 1;
		margin-left: 24rpx;
		min-width: 0;
		padding-top: 4rpx;
	}

	.ios-title {
		display: block;
		font-size: 38rpx;
		font-weight: 700;
		color: $ink;
		line-height: 1.2;
		letter-spacing: -0.8rpx;
	}

	.ios-subtitle {
		display: block;
		margin-top: 12rpx;
		font-size: 26rpx;
		font-weight: 400;
		color: $ink-muted;
		line-height: 1.4;
	}

	.ios-body {
		display: block;
		margin-top: 36rpx;
		font-size: 28rpx;
		font-weight: 400;
		color: $ink-body;
		line-height: 1.7;
		text-align: left;
	}

	.preline {
		white-space: pre-line;
	}

	/* rich-text 与正文对齐；内部标签字号由 HTML 决定时可被 rich-text 默认样式覆盖 */
	.ios-body-rich {
		display: block;
		margin-top: 36rpx;
		font-size: 28rpx;
		color: $ink-body;
		line-height: 1.7;
		text-align: left;
		word-break: break-word;
		overflow: hidden;
	}

	.footer-bar {
		flex-shrink: 0;
		padding: 16rpx 32rpx calc(20rpx + env(safe-area-inset-bottom));
		background: $surface;
		border-top: 1rpx solid $line;
		box-shadow: 0 -8rpx 32rpx rgba(0, 0, 0, 0.04);
		z-index: 998;
		box-sizing: border-box;
	}

	.footer-version {
		display: block;
		text-align: center;
		font-size: 24rpx;
		font-weight: 400;
		color: $ink-muted;
		line-height: 1.5;
		margin-bottom: 16rpx;
		letter-spacing: 0.2rpx;
	}

	.footer-version-num {
		font-weight: 600;
		color: $ink-strong;
		margin-left: 8rpx;
	}

	.footer-btn {
		height: 96rpx;
		border-radius: 48rpx;
		background: $ink;
		display: flex;
		align-items: center;
		justify-content: center;
	}

	.footer-btn--disabled {
		background: #d0d0d0;
	}

	.footer-btn-text {
		font-size: 30rpx;
		font-weight: 600;
		color: #ffffff;
		letter-spacing: 0.5rpx;
	}

	.footer-btn--disabled .footer-btn-text {
		color: #ffffff;
		opacity: 0.85;
	}

	.progress-mask {
		position: fixed;
		top: 0;
		left: 0;
		right: 0;
		bottom: 0;
		background: rgba(0, 0, 0, 0.5);
		z-index: 2000;
		display: flex;
		align-items: center;
		justify-content: center;
		padding: 48rpx;
		box-sizing: border-box;
	}

	.progress-card {
		position: relative;
		width: 100%;
		max-width: 620rpx;
		overflow: hidden;
		border-radius: 28rpx;
		padding: 52rpx 40rpx 44rpx;
		box-sizing: border-box;
		background: $surface;
		border: 1rpx solid $line;
		box-shadow: 0 24rpx 64rpx rgba(0, 0, 0, 0.18);
	}

	.progress-card-glow {
		position: absolute;
		top: -100rpx;
		left: 50%;
		transform: translateX(-50%);
		width: 400rpx;
		height: 200rpx;
		background: radial-gradient(
			ellipse at center,
			rgba(57, 150, 253, 0.12) 0%,
			transparent 65%
		);
		pointer-events: none;
	}

	.progress-top {
		position: relative;
		z-index: 1;
		margin-bottom: 40rpx;
	}

	.progress-pct-huge {
		display: flex;
		flex-direction: row;
		align-items: flex-end;
		justify-content: center;
		margin-bottom: 16rpx;
	}

	.pct-value {
		font-size: 88rpx;
		font-weight: 700;
		color: $ink;
		line-height: 0.9;
		letter-spacing: -3rpx;
		font-variant-numeric: tabular-nums;
	}

	.pct-unit {
		font-size: 36rpx;
		font-weight: 600;
		color: $ink-sub;
		line-height: 1.2;
		margin-left: 6rpx;
		margin-bottom: 10rpx;
	}

	.progress-headline {
		display: block;
		text-align: center;
		font-size: 30rpx;
		font-weight: 600;
		color: $ink-strong;
		letter-spacing: -0.3rpx;
	}

	.progress-stage {
		display: block;
		text-align: center;
		margin-top: 14rpx;
		font-size: 24rpx;
		color: $ink-muted;
		line-height: 1.5;
		min-height: 40rpx;
	}

	.progress-track-shell {
		position: relative;
		z-index: 1;
		margin-bottom: 20rpx;
	}

	.progress-track-bg {
		position: relative;
		height: 20rpx;
		border-radius: 10rpx;
		background: #e3e3e3;
		box-shadow: inset 0 2rpx 5rpx rgba(0, 0, 0, 0.08);
		overflow: hidden;
		box-sizing: border-box;
	}

	.progress-fill {
		position: relative;
		height: 100%;
		border-radius: 10rpx;
		background: linear-gradient(90deg, #5ba3fc 0%, $theme 45%, #2b7de0 100%);
		box-shadow:
			0 0 16rpx rgba(57, 150, 253, 0.35),
			inset 0 1rpx 0 rgba(255, 255, 255, 0.25);
		transition: width 0.1s linear, background 0.35s ease, box-shadow 0.35s ease;
		overflow: hidden;
	}

	.progress-shimmer {
		position: absolute;
		top: 0;
		left: 0;
		bottom: 0;
		width: 55%;
		background: linear-gradient(
			90deg,
			rgba(255, 255, 255, 0) 0%,
			rgba(255, 255, 255, 0.45) 45%,
			rgba(255, 255, 255, 0) 90%
		);
		animation: progress-shine 1.35s ease-in-out infinite;
	}

	@keyframes progress-shine {
		0% {
			transform: translateX(-100%);
		}
		100% {
			transform: translateX(220%);
		}
	}

	.progress-fill-head {
		position: absolute;
		top: 50%;
		right: 2rpx;
		transform: translateY(-50%);
		width: 8rpx;
		height: 55%;
		border-radius: 6rpx;
		background: rgba(255, 255, 255, 0.85);
		box-shadow: 0 0 14rpx rgba(255, 255, 255, 0.75);
	}

	.progress-hint {
		position: relative;
		z-index: 1;
		display: block;
		text-align: center;
		font-size: 22rpx;
		color: $ink-hint;
		letter-spacing: 0.5rpx;
	}
</style>
