import appSettings from '@/core/config/app-settings'
import { buildRestPath } from '@/core/config/api-paths'
import { get } from '@/core/http/http-client'
import { STORAGE } from '@/core/constants/storage-keys'

/**
 * 解析更新包 / App Store 等跳转地址。
 * 绝对 URI（https://、itms-apps:// 等）不再拼 CDN；相对路径才拼 cdnurl。
 */
function resolveUpdateAssetUrl(path) {
	let p = String(path || '').trim().replace(/^\uFEFF/, '')
	if (!p) return ''
	p = p.replace(/[\u200B-\u200D\uFEFF\u2060]/g, '')
	p = p.replace(/(https?)\uFF1A(\/\/)/gi, '$1:$2')
	p = p.replace(/(https?)(\/\/)/gi, (m, scheme, slashes, offset, str) => {
		const prev = offset > 0 ? str[offset - 1] : ''
		return prev === ':' ? m : `${scheme}:${slashes}`
	})
	p = p.replace(/^(https?):\/([^/])/i, '$1://$2')

	const absIdx = p.search(/https?:\/\//i)
	if (absIdx > 0) {
		p = p.slice(absIdx)
	}

	if (/^\/\//.test(p)) {
		p = `https:${p}`
	}
	if (/^[a-z][a-z0-9+.-]*:/i.test(p)) {
		return p
	}
	const base = String(appSettings.cdnurl || '').replace(/\/+$/, '')
	const rel = p.replace(/^\/+/, '')
	return base ? `${base}/${rel}` : p
}

function normalizeUpdateContent(data) {
	const copy = { ...data }
	copy.content = String(copy.content || '').replace(/(\r\n)|(\n)/g, '<br>')
	return copy
}

function buildDownloadLink(data) {
	const os = plus.os.name.toLowerCase()
	const linkKey = os === 'ios' ? 'iosLink' : 'androidLink'
	const raw = data[linkKey]
	if (!raw) return null
	const url = resolveUpdateAssetUrl(raw)
	if (!url) return null
	return {
		url,
		type: /\.wgt/i.test(url) ? 'wgt' : 'url'
	}
}

async function fetchRemoteUpdate() {
	const { data } = await get(buildRestPath('common/update'))
	if (!data || typeof data !== 'object') return null
	const remoteCode = Number(data.versionCode)
	const localCode = Number(appSettings.versionCode)
	if (!Number.isFinite(remoteCode) || remoteCode <= localCode) return null
	return data
}

function presentUpdate({ commit, dispatch }, data) {
	const meta = normalizeUpdateContent(data)
	commit('assign', { key: 'meta', value: meta })
	// #ifdef APP-PLUS
	const link = buildDownloadLink(meta)
	if (!link) {
		commit('assign', { key: 'visible', value: false })
		return { hasUpdate: true, hasLink: false, data: meta }
	}
	commit('assign', { key: 'link', value: link })
	if (Number(meta.enforce) === 1) {
		dispatch('startDownload')
		return { hasUpdate: true, hasLink: true, data: meta, enforced: true }
	}
	commit('assign', { key: 'visible', value: true })
	return { hasUpdate: true, hasLink: true, data: meta }
	// #endif
	// #ifndef APP-PLUS
	commit('assign', { key: 'visible', value: true })
	return { hasUpdate: true, hasLink: true, data: meta }
	// #endif
}

export default {
	namespaced: true,
	state: {
		visible: false,
		meta: {},
		link: {},
		download: {
			path: null,
			start: false,
			install: false,
			progress: 0,
			totalBytesWritten: 0,
			totalBytesExpectedToWrite: 0
		},
		task: null
	},
	mutations: {
		assign(state, { key, value }) {
			state[key] = value
		}
	},
	actions: {
		check({ dispatch }) {
			// #ifdef MP
			const mgr = uni.getUpdateManager()
			mgr.onUpdateReady(() => {
				uni.showModal({
					title: '更新提示',
					content: '新版本已就绪，是否重启应用？',
					success: (res) => res.confirm && mgr.applyUpdate()
				})
			})
			// #endif

			// #ifdef APP-PLUS
			const cache = uni.getStorageSync(STORAGE.update)
			if (cache?.ignore) return Promise.resolve({ hasUpdate: false, ignored: true })
			return dispatch('checkManual', { silent: true })
			// #endif

			// #ifndef APP-PLUS
			return Promise.resolve({ hasUpdate: false })
			// #endif
		},
		/**
		 * 手动检查更新（关于我们页）：忽略「不再提醒」，无新版本时由调用方 toast。
		 */
		async checkManual({ commit, dispatch }, { silent = false } = {}) {
			// #ifdef MP
			try {
				const mgr = uni.getUpdateManager()
				mgr.onCheckForUpdate((res) => {
					if (!res.hasUpdate && !silent) {
						uni.showToast({ title: '已是最新版本', icon: 'none' })
					}
				})
				mgr.onUpdateReady(() => {
					uni.showModal({
						title: '更新提示',
						content: '新版本已就绪，是否重启应用？',
						success: (r) => r.confirm && mgr.applyUpdate()
					})
				})
			} catch (_) {}
			return { hasUpdate: false }
			// #endif

			// #ifdef APP-PLUS
			try {
				const data = await fetchRemoteUpdate()
				if (!data) {
					return { hasUpdate: false }
				}
				return presentUpdate({ commit, dispatch }, data)
			} catch (e) {
				if (!silent) throw e
				return { hasUpdate: false, error: e }
			}
			// #endif

			// #ifndef APP-PLUS
			return { hasUpdate: false }
			// #endif
		},
		startDownload({ state, commit, dispatch }) {
			// #ifdef APP-PLUS
			const link = state.link
			if (!link || !link.url) return
			if (link.type === 'url') {
				plus.runtime.openURL(link.url)
				commit('assign', { key: 'visible', value: false })
				return
			}
			commit('assign', { key: 'visible', value: true })
			state.download.start = true
			state.task = uni.downloadFile({
				url: link.url,
				success: (res) => {
					if (res.statusCode !== 200) return
					uni.saveFile({
						tempFilePath: res.tempFilePath,
						success: (file) => {
							uni.setStorageSync(STORAGE.update, { path: file.savedFilePath, ignore: false })
							state.download.path = file.savedFilePath
							state.download.start = false
							state.task?.abort?.()
							state.task = null
							dispatch('installPackage')
						}
					})
				},
				fail: () => {
					state.download.start = false
					uni.showToast({ title: '下载失败', icon: 'none' })
				}
			})
			state.task.onProgressUpdate((p) => {
				state.download.progress = p.progress
				state.download.totalBytesWritten = p.totalBytesWritten
				state.download.totalBytesExpectedToWrite = p.totalBytesExpectedToWrite
			})
			// #endif
		},
		installPackage({ state, commit }) {
			// #ifdef APP-PLUS
			state.download.install = true
			plus.runtime.install(state.download.path, state.visible ? {} : { force: true }, () => {
				commit('assign', { key: 'visible', value: false })
				state.download.install = false
				uni.showModal({
					title: '更新完成',
					content: '应用将重启以完成更新',
					showCancel: false,
					complete: () => plus.runtime.restart()
				})
			}, (e) => {
				state.download.install = false
				uni.showToast({ title: `更新失败：${e.message}`, icon: 'none' })
			})
			// #endif
		},
		ignore({ commit }) {
			uni.setStorageSync(STORAGE.update, { path: null, ignore: true })
			commit('assign', { key: 'visible', value: false })
		}
	}
}
