<template>
	<view>
		<u-popup v-model="localVisible" mode="bottom" :height="height" :closeable="true" @close="handleClose">
			<view class="print-popup">
				<!-- 标题栏 -->
				<view class="print-popup__header">
					<text class="print-popup__title">标签打印</text>
				</view>

				<!-- 隐藏画布，用于标签绘制 -->
				<canvas
					:id="canvasId"
					:canvas-id="canvasId"
					type="2d"
					:style="canvasStyles"
				/>

				<scroll-view scroll-y="true" class="print-popup__body">
					<!-- 预览区域 -->
					<view class="preview-section" v-if="previewList.length > 0">
						<text class="preview-section__title">打印预览</text>
						<view class="preview-section__list">
							<image
								v-for="item in previewList"
								:src="item.value"
								:key="item.key"
								class="preview-img"
								mode="widthFix"
							/>
						</view>
					</view>

					<!-- 打印机选择 -->
					<view class="setting-row">
						<text class="setting-row__label">打印机</text>
						<view class="setting-row__right">
							<picker :value="deviceIndex" :range="deviceList" @change="onDeviceChanged" range-key="name">
								<view class="setting-row__picker">
									<text
										class="setting-row__value"
										:class="{ 'setting-row__value--empty': !deviceList[deviceIndex]?.deviceId }"
									>
										{{ deviceList[deviceIndex]?.name || '未检测到打印机' }}
									</text>
									<u-icon name="arrow-down" :size="24" color="#999" />
								</view>
							</picker>
						</view>
					</view>

					<!-- 出纸方向 -->
					<view class="setting-row">
						<text class="setting-row__label">出纸方向</text>
						<view class="setting-row__right">
							<picker :value="rotationIndex" :range="rotationList" @change="onRotationChanged" range-key="name">
								<view class="setting-row__picker">
									<text class="setting-row__value">{{ rotationList[rotationIndex]?.name || '' }}</text>
									<u-icon name="arrow-down" :size="24" color="#999" />
								</view>
							</picker>
						</view>
					</view>

					<!-- 纸张类型 -->
					<view class="setting-row">
						<text class="setting-row__label">纸张类型</text>
						<view class="setting-row__right">
							<picker :value="gapIndex" :range="gapList" @change="onGapTypeChanged" range-key="name">
								<view class="setting-row__picker">
									<text class="setting-row__value">{{ gapList[gapIndex]?.name || '' }}</text>
									<u-icon name="arrow-down" :size="24" color="#999" />
								</view>
							</picker>
						</view>
					</view>

					<!-- 打印浓度 -->
					<view class="setting-row">
						<text class="setting-row__label">打印浓度</text>
						<view class="setting-row__right">
							<picker :value="darknessIndex" :range="darknessList" @change="onDarknessChanged" range-key="name">
								<view class="setting-row__picker">
									<text class="setting-row__value">{{ darknessList[darknessIndex]?.name || '' }}</text>
									<u-icon name="arrow-down" :size="24" color="#999" />
								</view>
							</picker>
						</view>
					</view>

					<!-- 打印速度 -->
					<view class="setting-row">
						<text class="setting-row__label">打印速度</text>
						<view class="setting-row__right">
							<picker :value="speedIndex" :range="speedList" @change="onPrintSpeedChanged" range-key="name">
								<view class="setting-row__picker">
									<text class="setting-row__value">{{ speedList[speedIndex]?.name || '' }}</text>
									<u-icon name="arrow-down" :size="24" color="#999" />
								</view>
							</picker>
						</view>
					</view>

					<!-- 灰度阈值 -->
					<view class="setting-row">
						<text class="setting-row__label">灰度阈值</text>
						<view class="setting-row__right">
							<input class="setting-row__input" type="number" v-model="threshold" />
						</view>
					</view>

					<!-- JSON配置模式 -->
					<view class="setting-row">
						<text class="setting-row__label">JSON配置</text>
						<view class="setting-row__right">
							<switch :checked="jsonMode" @change="onPrintModeChanged" style="transform: scale(0.75)" />
						</view>
					</view>
				</scroll-view>

				<!-- 底部操作按钮 -->
				<view class="print-popup__footer">
					<view class="footer-grid">
						<view class="footer-btn" @click="handleStartDiscovery">
							<u-icon name="search" :size="30" color="#2979ff" />
							<text>搜索打印机</text>
						</view>
						<view class="footer-btn" @click="handleStopDiscovery">
							<u-icon name="close-circle" :size="30" color="#f56c6c" />
							<text>停止搜索</text>
						</view>
						<view class="footer-btn" @click="handleOpenPrinter">
							<u-icon name="link" :size="30" color="#2979ff" />
							<text>连接打印机</text>
						</view>
						<view class="footer-btn" @click="handleClosePrinter">
							<u-icon name="minus-circle" :size="30" color="#f56c6c" />
							<text>断开连接</text>
						</view>
						<view class="footer-btn footer-btn--print" @click="handlePrintLabel">
							<u-icon v-if="!printing" name="printer" :size="30" color="#fff" />
							<u-icon v-else name="reload" :size="30" color="#fff" class="spinning" />
							<text>{{ printing ? '打印中...' : '打印' }}</text>
						</view>
					</view>
				</view>
			</view>
		</u-popup>
	</view>
</template>

<script setup>
	import { ref, watch, onBeforeUnmount, nextTick, getCurrentInstance } from 'vue'
	import {
		DrawType,
		LPAPIFactory
	} from '@/assets/js/js_sdk'
	import '@/utils/bwipjs-polyfill.js'
	import bwipjs from '@/scanpages/assets/bwip-js/bwip-js.mjs'

	// ===== Props =====
	const props = defineProps({
		modelValue: { type: Boolean, default: false },
		height: { type: String, default: '85%' },
		labelWidth: { type: Number, default: 50 },
		labelHeight: { type: Number, default: 30 },
		margin: { type: Number, default: 2 },
		barcodeText: { type: String, default: '12345678' },
		barcodeTopText: { type: String, default: '' },
		barcodeBottomText: { type: String, default: '' }
	})

	const emit = defineEmits(['update:modelValue', 'close', 'printed', 'error'])

	const instance = getCurrentInstance()

	// ===== 响应式数据 =====
	const canvasId = 'lpapi-ble-popup'
	const labelW = ref(960)
	const labelH = ref(960)
	const canvasStyles = ref({})
	const localVisible = ref(false)
	const deviceList = ref([{ name: '未检测到打印机' }])
	const deviceIndex = ref(0)
	const rotationIndex = ref(1)
	const rotationList = [
		{ name: '横向打印', value: 0 },
		{ name: '右转90度', value: 90 },
		{ name: '转转180度', value: 180 },
		{ name: '左转90度', value: 270 }
	]
	const gapList = [
		{ name: '随打印机设置', value: 255 },
		{ name: '小票纸', value: 0 },
		{ name: '不干胶', value: 2 },
		{ name: '卡纸', value: 3 }
	]
	const gapIndex = ref(0)
	const darknessList = [
		{ name: '随打印机设置', value: 255 },
		{ name: '6 (正常)', value: 6 },
		{ name: '7', value: 7 },
		{ name: '8', value: 8 },
		{ name: '9', value: 9 },
		{ name: '10 (较浓)', value: 10 },
		{ name: '11', value: 11 },
		{ name: '12', value: 12 },
		{ name: '13', value: 13 },
		{ name: '14', value: 14 },
		{ name: '15 (最浓)', value: 15 }
	]
	const darknessIndex = ref(0)
	const speedList = [
		{ name: '随打印机设置', value: 255 },
		{ name: '最慢', value: 1 },
		{ name: '较慢', value: 2 },
		{ name: '正常', value: 3 },
		{ name: '较快', value: 4 },
		{ name: '最快', value: 5 }
	]
	const speedIndex = ref(0)
	const threshold = ref(128)
	const jsonMode = ref(false)
	const printing = ref(false)
	const isWeiXin = ref(false)
	const isAlipay = ref(false)
	const lpapi = ref(undefined)
	const context = ref(undefined)
	const previewList = ref([])

	// ===== 取值辅助 =====
	const getDevice = () => {
		return deviceList.value[deviceIndex.value]
	}
	const getOrientation = () => {
		return rotationList[rotationIndex.value]?.value || 0
	}
	const getGapType = () => {
		return gapList[gapIndex.value]?.value || 255
	}
	const getPrintDarkness = () => {
		return darknessList[darknessIndex.value]?.value || 255
	}
	const getPrintSpeed = () => {
		return speedList[speedIndex.value]?.value || 255
	}
	const getThreshold = () => {
		return Number(threshold.value)
	}

	// ===== Canvas 样式 =====
	const updateCanvasStyle = () => {
		var pos = { position: 'absolute' }
		if (isWeiXin.value) {
			pos.left = '-999999rpx'
			pos.top = '-999999rpx'
			pos.opacity = '0'
			pos.pointerEvents = 'none'
			pos.zIndex = '-1'
		} else {
			pos.left = '-999999rpx'
			pos.top = '-999999rpx'
		}
		canvasStyles.value = Object.assign(
			{ width: labelW.value + 'px', height: labelH.value + 'px' },
			pos
		)
	}

	// ===== Canvas 更新 =====
	const updateCanvas = (jobInfo) => {
		return new Promise((resolve) => {
			if (jobInfo && jobInfo.canvas) {
				labelW.value = jobInfo.canvas.width
				labelH.value = jobInfo.canvas.height
				updateCanvasStyle()
			}
			setTimeout(() => resolve(true), 100)
		})
	}

	// ===== PDF417 条码生成（使用 bwip-js）=====
	const _renderBwipjs = (canvas, opts) => {
		try {
			bwipjs.toCanvas(canvas, opts)
		} catch (e) {
			if (typeof canvas.getContext === 'function') {
				var proto = Object.getPrototypeOf(canvas)
				var ctorName = proto && proto.constructor && proto.constructor.name
				if (ctorName && !(/HTMLCanvasElement|OffscreenCanvas/).test(ctorName)) {
					try {
						Object.defineProperty(proto.constructor, 'name', { configurable: true, value: 'HTMLCanvasElement' })
						bwipjs.toCanvas(canvas, opts)
						Object.defineProperty(proto.constructor, 'name', { configurable: true, value: ctorName })
						return
					} catch (e2) {}
				}
			}
			throw e
		}
	}

	const generateBarcodeImage = (text, widthMM, heightMM) => {
		return new Promise((resolve, reject) => {
			var dpi = 203
			var pxPerMM = dpi / 25.4
			var tw = Math.round(widthMM * pxPerMM)
			var th = Math.round(heightMM * pxPerMM)

			var baseScale = Math.max(3, Math.floor(tw / 60))
			var bwipOpts = {
				bcid: 'pdf417',
				text: text,
				scale: baseScale,
				includetext: false,
				padding: 0
			}

			nextTick(() => {
				var query = uni.createSelectorQuery().in(instance)
				query.select('#' + canvasId).node().exec((res) => {
					try {
						if (!res || !res[0] || !res[0].node) {
							return reject(new Error('canvas 未找到'))
						}
						var canvas = res[0].node
						_renderBwipjs(canvas, bwipOpts)
						console.log('bwip-js PDF417:', { w: canvas.width, h: canvas.height, tw: tw, th: th })

						uni.canvasToTempFilePath({
							canvas: canvas,
							x: 0, y: 0,
							width: canvas.width,
							height: canvas.height,
							success: (t) => {
								if (isWeiXin.value) {
									var img = canvas.createImage()
									img.src = t.tempFilePath
									img.onload = () => { resolve(img) }
									img.onerror = (e) => { reject(e) }
								} else {
									resolve(t.tempFilePath)
								}
							},
							fail: (e) => { reject(e) }
						})
					} catch (e) { reject(e) }
				})
			})
		})
	}

	// ===== 标签绘制（总入口） =====
	const printLabel = (isPreview) => {
		if (jsonMode.value) {
			return printLabelWithJsonMode(isPreview)
		} else {
			return printLabelWithDrawMode(isPreview)
		}
	}

	const printLabelWithDrawMode = async (isPreview) => {
		var api = lpapi.value
		var lW = props.labelWidth
		var lH = props.labelHeight
		var m = props.margin
		var uW = lW - m * 2

		var jobInfo = api.startJob({
			context: context.value,
			width: lW,
			height: lH,
			orientation: getOrientation(),
			isPreview: isPreview
		})
		await updateCanvas(jobInfo)

		if (props.barcodeTopText) {
			api.drawText({
				text: props.barcodeTopText,
				x: m,
				y: m,
				width: uW,
				height: 5,
				fontHeight: 4,
				horizontalAlignment: 1
			})
		}

		var barcodeY = m + (props.barcodeTopText ? 7 : 0)
		var barcodeH = lH - m * 2 - (props.barcodeTopText ? 7 : 0) - (props.barcodeBottomText ? 7 : 0)
		try {
			var img = await generateBarcodeImage(props.barcodeText, uW, barcodeH)
			api.drawImage({ img: img, x: m, y: barcodeY, width: uW, height: barcodeH })
		} catch (e) {
			console.error('PDF417 绘制失败:', e)
		}

		if (props.barcodeBottomText) {
			var by = lH - m - 5
			api.drawText({
				text: props.barcodeBottomText,
				x: m,
				y: by,
				width: uW,
				height: 5,
				fontHeight: 3.5,
				horizontalAlignment: 1
			})
		}

		return api.commitJob({
			gapType: getGapType(),
			printDarkness: getPrintDarkness(),
			printSpeed: getPrintSpeed()
		})
	}

	const printLabelWithJsonMode = (isPreview) => {
		var device = getDevice()
		var lW = props.labelWidth
		var lH = props.labelHeight
		var m = props.margin
		var uW = lW - m * 2

		var jobPage = []

		if (props.barcodeTopText) {
			jobPage.push({
				type: DrawType.text,
				text: props.barcodeTopText,
				x: m,
				y: m,
				width: uW,
				height: 5,
				fontHeight: 4,
				horizontalAlignment: 1
			})
		}

		var barcodeY = m + (props.barcodeTopText ? 7 : 0)
		var barcodeH = lH - m * 2 - (props.barcodeTopText ? 7 : 0) - (props.barcodeBottomText ? 7 : 0)
		return generateBarcodeImage(props.barcodeText, uW, barcodeH).then((img) => {
			jobPage.push({
				type: DrawType.image,
				img: img,
				x: m,
				y: barcodeY,
				width: uW,
				height: barcodeH
			})

			if (props.barcodeBottomText) {
				jobPage.push({
					type: DrawType.text,
					text: props.barcodeBottomText,
					x: m,
					y: lH - m - 5,
					width: uW,
					height: 5,
					fontHeight: 3.5,
					horizontalAlignment: 1
				})
			}

			return lpapi.value.print({
				jobInfo: {
					jobWidth: lW,
					jobHeight: lH,
					orientation: getOrientation(),
					isPreview: isPreview
				},
				printerInfo: {
					name: device ? device.name : undefined,
					deviceId: device ? device.deviceId : undefined
				},
				jobPage: jobPage,
				onJobCreated: (jobInfo) => {
					return updateCanvas(jobInfo)
				},
				onPageComplete: (res) => {
					// 标签页面渲染完毕时的回调
				},
				onPagePrintComplete: (res) => {
					// 标签页面打印完毕时的回调
				}
			})
		})
	}

	// ===== 初始化 =====
	const initPrinter = () => {
		// #ifdef MP-WEIXIN
		isWeiXin.value = true
		// #endif
		// #ifdef MP-ALIPAY
		isAlipay.value = true
		// #endif

		updateCanvasStyle()

		lpapi.value = LPAPIFactory.getInstance({ showLog: 4 })

		lpapi.value.startDiscovery({
			timeout: 0,
			deviceFound: (devices) => {
				if (devices && devices.length > 0) {
					deviceList.value.splice(0)
					deviceList.value.push(...devices)
				}
			}
		})

		if (isWeiXin.value) {
			context.value = lpapi.value.createDrawContext()
		} else {
			context.value = lpapi.value.createDrawContext({ canvasId: canvasId })
		}
		if (context.value) {
			lpapi.value.setDrawContext(context.value)
			handlePreviewLabel()
		}
	}

	// ===== 选择器变更 =====
	const onDeviceChanged = (e) => {
		deviceIndex.value = e ? e.detail.value : 0
	}
	const onRotationChanged = (e) => {
		rotationIndex.value = e ? e.detail.value : 0
	}
	const onGapTypeChanged = (e) => {
		gapIndex.value = e ? e.detail.value : 0
	}
	const onDarknessChanged = (e) => {
		darknessIndex.value = e ? e.detail.value : 0
	}
	const onPrintSpeedChanged = (e) => {
		speedIndex.value = e ? e.detail.value : 0
	}
	const onPrintModeChanged = (e) => {
		jsonMode.value = e.detail.value
		handlePreviewLabel()
	}

	// ===== 打印机操作 =====
	const handleStartDiscovery = () => {
		uni.showLoading({ title: '正在搜索打印机...' })
		lpapi.value.startDiscovery({
			timeout: 5000,
			deviceFound: (devices) => {
				if (devices && devices.length > 0) {
					deviceList.value.splice(0)
					deviceList.value.push(...devices)
				}
			},
			adapterStateChange: (result) => {
				if (!result.discovering) {
					uni.hideLoading()
				}
			}
		})
	}
	const handleStopDiscovery = () => {
		lpapi.value.stopDiscovery()
		uni.hideLoading()
	}
	const handleOpenPrinter = () => {
		const currDevice = getDevice()
		if (!currDevice || !currDevice.deviceId) {
			uni.showToast({ title: '未检测到打印机', icon: 'none' })
			return
		}
		uni.showLoading({ title: '正在连接打印机...' })
		lpapi.value.openPrinter({
			name: currDevice.name,
			deviceId: currDevice.deviceId,
			tryTimes: isAlipay.value ? 1 : 5,
			success: (resp) => {
				console.log('---- 【打印机连接成功】')
				uni.hideLoading()
				uni.showToast({ title: '打印机连接成功！', icon: 'success' })
			},
			fail: (resp) => {
				console.warn('---- 【打印机连接失败】：', JSON.stringify(resp))
				uni.hideLoading()
				uni.showToast({ title: '打印机连接失败！', icon: 'error' })
			}
		})
	}
	const handleClosePrinter = () => {
		console.log('---- 关闭打印机！')
		lpapi.value.closePrinter()
	}

	// ===== 预览 =====
	const handlePreviewLabel = () => {
		previewList.value.splice(0)
		printLabel(true).then((res) => {
			if (res.statusCode === 0) {
				res.previewData.forEach((v) => {
					previewList.value.push({
						value: v,
						key: Date.now() + '-' + Math.random() * 1000
					})
				})
			} else {
				console.warn('预览失败:', res)
			}
		})
	}

	// ===== 打印 =====
	const handlePrintLabel = async () => {
		printing.value = true
		try {
			var d = getDevice()
			if (!d || !d.deviceId) {
				printing.value = false
				return uni.showToast({ title: '未检测到打印机', icon: 'none' })
			}
		await new Promise((resolve, reject) => {
			lpapi.value.openPrinter({
				name: d.name,
				deviceId: d.deviceId,
				tryTimes: 5,
				success: resolve,
				fail: reject
			})
		})
		var res = await printLabel(false)
		if (res.statusCode === 0) {
			uni.showToast({ title: '打印成功！', icon: 'success' })
			emit('printed', { statusCode: 0 })
		} else {
			uni.showToast({ title: res.errMsg || '打印失败！', icon: 'error' })
			emit('error', res)
		}
	} catch (e) {
		console.error('打印异常:', e)
		uni.showToast({ title: '打印机连接失败！', icon: 'error' })
		emit('error', e)
	} finally {
		printing.value = false
	}
	}

	// ===== 关闭弹窗 =====
	const handleClose = () => {
		handleClosePrinter()
		emit('close')
	}

	// ===== 监听器 =====
	watch(() => props.modelValue, (val) => {
		localVisible.value = val
		if (val) nextTick(() => initPrinter())
	})
	watch(localVisible, (val) => {
		emit('update:modelValue', val)
	})

	onBeforeUnmount(() => {
		if (lpapi.value) {
			lpapi.value.closePrinter()
			lpapi.value.stopDiscovery()
		}
	})
</script>

<style lang="scss" scoped>
.print-popup { display: flex; flex-direction: column; height: 100%; background: #f5f6f8;
	&__header { flex-shrink: 0; padding: 28rpx 32rpx 20rpx; text-align: center; border-bottom: 1rpx solid #e8ecf1; }
	&__title { font-size: 32rpx; font-weight: 700; color: #1a1a2e; }
	&__body { flex: 1; overflow-y: auto; overflow-x: hidden; box-sizing: border-box; padding: 20rpx 24rpx; }
	&__footer { flex-shrink: 0; padding: 12rpx 24rpx; padding-bottom: calc(12rpx + env(safe-area-inset-bottom)); background: #fff; border-top: 1rpx solid #e8ecf1; }
}
.footer-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12rpx; }
.footer-btn { display: flex; align-items: center; justify-content: center; gap: 8rpx; height: 76rpx; font-size: 26rpx; font-weight: 500; border-radius: 12rpx; background: #f5f6f8; color: #1a1a2e; transition: transform .15s, box-shadow .15s;
	&:active { transform: scale(.95); opacity: .85; }
	&--print { background: linear-gradient(135deg, #2979ff, #5a9aff); color: #fff; box-shadow: 0 4rpx 16rpx rgba(41,121,255,.35);     grid-column: 1 / -1;}
}
.spinning { animation: spin .8s linear infinite; }
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
.setting-row { display: flex; align-items: center; justify-content: space-between; box-sizing: border-box; padding: 22rpx 24rpx; margin-bottom: 12rpx; background: #fff; border-radius: 12rpx;
	&__label { font-size: 26rpx; color: #6b7c93; flex-shrink: 0; width: 160rpx; }
	&__right { flex: 1; display: flex; justify-content: flex-end; }
	&__picker { display: flex; align-items: center; gap: 8rpx; }
	&__value { font-size: 26rpx; color: #1a1a2e; font-weight: 500;
		&--empty { color: #b0bec5; }
	}
	&__input { flex: 1; font-size: 26rpx; color: #1a1a2e; text-align: right; padding: 4rpx 0; }
}
.preview-section { margin-bottom: 24rpx; padding: 20rpx; background: #fff; border-radius: 12rpx; overflow: hidden;
	&__title { display: block; font-size: 26rpx; font-weight: 600; color: #1a1a2e; margin-bottom: 16rpx; }
	&__list { display: flex; flex-wrap: wrap; justify-content: center; gap: 16rpx; }
}
.preview-img { width: 100%; border: 1rpx dashed #ccc; border-radius: 4rpx; }
</style>
