<template>
	<view class="scan-page">
		<!-- 扫码区域 -->
		<view class="scan-window">
			<video id="scanVideo" ref="videoRef" class="scan-video" autoplay playsinline muted></video>
			<canvas id="scanCanvas" ref="canvasRef" class="hidden-canvas" style="display: none;"></canvas>
			
			<!-- 扫描装饰 -->
			<view class="scan-mask">
				<view class="scan-frame">
					<view class="scan-line"></view>
					<view class="corner top-left"></view>
					<view class="corner top-right"></view>
					<view class="corner bottom-left"></view>
					<view class="corner bottom-right"></view>
				</view>
			</view>
			
			<view class="scan-tip">{{ tipText }}</view>
		</view>

		<!-- 底部操作栏 -->
		<view class="bottom-actions">
			<view class="action-item" @click="chooseImage">
				<uv-icon name="photo" size="28" color="#fff"></uv-icon>
				<text>相册</text>
			</view>
			
			<view class="action-item" @click="toggleInput">
				<uv-icon name="edit-pen" size="28" color="#fff"></uv-icon>
				<text>手动输入</text>
			</view>
			
			<view class="action-item" @click="goBack">
				<uv-icon name="arrow-left" size="28" color="#fff"></uv-icon>
				<text>返回</text>
			</view>
		</view>

		<!-- 手动输入弹窗 -->
		<uv-popup ref="inputPopup" mode="center" round="16" :closeOnClickOverlay="true">
			<view class="input-dialog">
				<view class="dialog-title">手动输入设备号</view>
				<input 
					v-model="manualDeviceNo" 
					placeholder="请输入设备上的编号" 
					class="device-input"
					type="text"
					focus
				/>
				<view class="dialog-btns">
					<button class="cancel-btn" @click="closeInput">取消</button>
					<button class="confirm-btn" @click="confirmManualInput">确定</button>
				</view>
			</view>
		</uv-popup>
	</view>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { getQueryString } from '@/util/index.js';

const videoRef = ref(null);
const canvasRef = ref(null);
const inputPopup = ref(null);
const manualDeviceNo = ref('');
const tipText = ref('正在启动扫描...');
const scanning = ref(false);

let stream = null;
let animationId = null;

// 动态加载解码库
const loadJsQR = () => {
	return new Promise((resolve, reject) => {
		if (window.jsQR) {
			resolve(window.jsQR);
			return;
		}
		const script = document.createElement('script');
		script.src = 'https://cdn.jsdelivr.net/npm/jsqr@1.4.0/dist/jsQR.min.js';
		script.onload = () => resolve(window.jsQR);
		script.onerror = (e) => {
			console.error('jsQR 加载失败:', e);
			reject(new Error('解码组件加载失败，请检查网络'));
		};
		document.head.appendChild(script);
	});
};

const initScan = async () => {
	try {
		// 1. 检查环境
		if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
			throw new Error('您的浏览器不支持摄像头访问，请使用微信扫描或手动输入');
		}

		// 2. 加载解码库
		const jsQR = await loadJsQR();

		// 3. 启动摄像头 - 尝试逐步降低约束
		let constraints = {
			video: { 
				facingMode: 'environment',
				width: { ideal: 1280 },
				height: { ideal: 720 }
			}
		};
		
		try {
			stream = await navigator.mediaDevices.getUserMedia(constraints);
		} catch (e) {
			console.warn('尝试理想约束失败，降级请求:', e);
			// 降级：仅请求视频，不限制分辨率和模式
			stream = await navigator.mediaDevices.getUserMedia({ video: true });
		}
		
		if (videoRef.value) {
			const video = videoRef.value;
			// 关键：在赋值 srcObject 之前先重置
			video.pause();
			video.srcObject = stream;
			
			// 使用更稳健的事件监听
			const startPlay = () => {
				video.play().then(() => {
					console.log('视频开始播放');
					scanning.value = true;
					tipText.value = '将二维码放入框内即可自动扫描';
					tick(jsQR);
				}).catch(e => {
					console.error('视频播放 Promise 失败:', e);
					// 如果是由于用户交互限制，提示用户点击
					tipText.value = '请点击屏幕启动扫描';
				});
			};

			if (video.readyState >= 2) {
				startPlay();
			} else {
				video.onloadedmetadata = startPlay;
			}
		}
	} catch (err) {
		console.error('摄像头初始化失败:', err);
		let errMsg = '摄像头开启失败';
		if (err.name === 'NotAllowedError' || err.name === 'PermissionDeniedError') {
			errMsg = '请授予摄像头访问权限后重试';
		} else if (err.name === 'NotFoundError' || err.name === 'DevicesNotFoundError') {
			errMsg = '未找到可用的摄像头';
		} else if (location.protocol !== 'https:' && location.hostname !== 'localhost') {
			errMsg = '非加密连接(HTTPS)无法开启摄像头';
		}
		
		tipText.value = errMsg;
		uni.showModal({
			title: '提示',
			content: errMsg,
			showCancel: false,
			success: () => {
				// 如果失败且无法恢复，引导手动输入
				toggleInput();
			}
		});
	}
};

const tick = (jsQR) => {
	if (!scanning.value) return;

	const video = videoRef.value;
	const canvas = canvasRef.value;
	
	if (video && video.readyState === video.HAVE_ENOUGH_DATA && canvas) {
		const ctx = canvas.getContext('2d');
		canvas.height = video.videoHeight;
		canvas.width = video.videoWidth;
		ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
		
		const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
		const code = jsQR(imageData.data, imageData.width, imageData.height, {
			inversionAttempts: 'dontInvert',
		});
		
		if (code && code.data) {
			console.log('扫码结果:', code.data);
			handleSuccess(code.data);
			return; // 停止循环
		}
	}
	animationId = requestAnimationFrame(() => tick(jsQR));
};

const handleSuccess = (result) => {
	stopScan();
	
	// 通过全局事件通知首页
	uni.$emit('h5ScanSuccess', {
		result: result,
		scanType: 'QR_CODE'
	});
	
	uni.navigateBack();
};

const stopScan = () => {
	scanning.value = false;
	if (stream) {
		stream.getTracks().forEach(track => track.stop());
		stream = null;
	}
	if (animationId) {
		cancelAnimationFrame(animationId);
		animationId = null;
	}
};

const chooseImage = () => {
	uni.chooseImage({
		count: 1,
		sourceType: ['album'],
		success: async (res) => {
			uni.showLoading({ title: '正在识别...' });
			try {
				const jsQR = await loadJsQR();
				const img = new Image();
				img.src = res.tempFilePaths[0];
				img.onload = () => {
					const canvas = document.createElement('canvas');
					const ctx = canvas.getContext('2d');
					canvas.width = img.width;
					canvas.height = img.height;
					ctx.drawImage(img, 0, 0);
					const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
					const code = jsQR(imageData.data, imageData.width, imageData.height);
					
					uni.hideLoading();
					if (code && code.data) {
						handleSuccess(code.data);
					} else {
						uni.showToast({ title: '未识别到二维码', icon: 'none' });
					}
				};
			} catch (e) {
				uni.hideLoading();
				uni.showToast({ title: '识别出错', icon: 'none' });
			}
		}
	});
};

const toggleInput = () => {
	inputPopup.value.open();
};

const closeInput = () => {
	inputPopup.value.close();
};

const confirmManualInput = () => {
	if (!manualDeviceNo.value) return;
	
	let deviceNo = manualDeviceNo.value.trim();
	if (deviceNo.includes('deviceNo=')) {
		deviceNo = getQueryString(deviceNo, 'deviceNo') || deviceNo;
	}
	
	closeInput();
	stopScan();
	
	uni.$emit('h5ScanSuccess', {
		result: deviceNo,
		scanType: 'MANUAL'
	});
	
	uni.navigateBack();
};

const goBack = () => {
	uni.navigateBack();
};

onMounted(() => {
	initScan();
});

onUnmounted(() => {
	stopScan();
});
</script>

<style lang="scss" scoped>
.scan-page {
	width: 100vw;
	height: 100vh;
	background: #000;
	position: relative;
	overflow: hidden;
}

.scan-window {
	width: 100%;
	height: 100%;
	position: relative;
	
	.scan-video {
		width: 100%;
		height: 100%;
		object-fit: cover;
	}
}

.scan-mask {
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	background: rgba(0, 0, 0, 0.4);
	display: flex;
	align-items: center;
	justify-content: center;
	pointer-events: none;
	
	.scan-frame {
		width: 500rpx;
		height: 500rpx;
		position: relative;
		box-shadow: 0 0 0 1000px rgba(0, 0, 0, 0.4);
		
		.scan-line {
			width: 100%;
			height: 4rpx;
			background: linear-gradient(to right, transparent, #3EAB64, transparent);
			position: absolute;
			top: 0;
			animation: scanMove 3s linear infinite;
		}
		
		.corner {
			position: absolute;
			width: 40rpx;
			height: 40rpx;
			border: 6rpx solid #3EAB64;
		}
		
		.top-left { top: -2rpx; left: -2rpx; border-right: none; border-bottom: none; }
		.top-right { top: -2rpx; right: -2rpx; border-left: none; border-bottom: none; }
		.bottom-left { bottom: -2rpx; left: -2rpx; border-right: none; border-top: none; }
		.bottom-right { bottom: -2rpx; right: -2rpx; border-left: none; border-top: none; }
	}
}

@keyframes scanMove {
	0% { top: 0; }
	100% { top: 100%; }
}

.scan-tip {
	position: absolute;
	top: 15%;
	left: 0;
	right: 0;
	text-align: center;
	color: #fff;
	font-size: 28rpx;
	padding: 0 40rpx;
}

.bottom-actions {
	position: absolute;
	bottom: 80rpx;
	left: 0;
	right: 0;
	display: flex;
	justify-content: space-around;
	padding: 0 60rpx;
	
	.action-item {
		display: flex;
		flex-direction: column;
		align-items: center;
		gap: 12rpx;
		
		text {
			color: #fff;
			font-size: 24rpx;
		}
	}
}

.input-dialog {
	width: 600rpx;
	background: #fff;
	padding: 40rpx;
	border-radius: 24rpx;
	
	.dialog-title {
		font-size: 32rpx;
		font-weight: 600;
		text-align: center;
		margin-bottom: 40rpx;
	}
	
	.device-input {
		height: 88rpx;
		background: #F8F9FA;
		border-radius: 12rpx;
		padding: 0 24rpx;
		font-size: 28rpx;
		border: 1rpx solid #eee;
		margin-bottom: 40rpx;
	}
	
	.dialog-btns {
		display: flex;
		gap: 20rpx;
		
		button {
			flex: 1;
			height: 80rpx;
			display: flex;
			align-items: center;
			justify-content: center;
			font-size: 28rpx;
			border-radius: 40rpx;
			border: none;
		}
		
		.cancel-btn {
			background: #f5f5f5;
			color: #666;
		}
		
		.confirm-btn {
			background: #3EAB64;
			color: #fff;
		}
	}
}
</style>
