<template>
	<view class="content">
		<view class="panel-card">
			<view class="panel-grid">
				<view class="panel-cell" @click="showApiUrlDialog()">
					<text class="cell-icon">短信</text>
					<text class="cell-label">上传接口</text>
					<text class="cell-desc">{{ apiUrl ? '已配置' : '未配置' }}</text>
				</view>
				<view class="panel-cell">
					<picker :range="intervalLabels" :value="intervalIndex" @change="onIntervalChange" style="width: 100%;">
						<view class="cell-inner">
							<text class="cell-icon">轮询</text>
							<text class="cell-label">轮询间隔</text>
							<text class="cell-desc">{{ intervalLabels[intervalIndex] }}</text>
						</view>
					</picker>
				</view>
				<view class="panel-cell" @click="manualFetch">
					<text class="cell-icon">刷新</text>
					<text class="cell-label">手动执行</text>
					<text class="cell-desc">{{ loading ? '读取中...' : permissionStatus }}</text>
				</view>
			</view>
			<view class="panel-switches">
				<view class="switch-item switch-left">
					<text class="switch-label">轮询</text>
					<switch :checked="polling" @change="onSwitchChange" color="#2563eb" />
				</view>
				<view class="switch-item switch-right" v-if="polling">
					<text class="countdown-text">{{ countdownText }}</text>
				</view>
			</view>
		</view>

		<view class="modal-mask" v-if="showApiModal" @click="showApiModal = false">
			<view class="modal-box" @click.stop>
				<text class="modal-title">设置上传接口</text>
				<textarea
					class="modal-input"
					v-model="apiUrlTemp"
					placeholder="请输入接口地址"
					auto-height
					:maxlength="-1"
				/>
				<view class="modal-btns">
					<button class="modal-btn" type="default" @click="showApiModal = false">取消</button>
					<button class="modal-btn" type="primary" @click="confirmApiUrl">确定</button>
				</view>
			</view>
		</view>

		<view class="stats-card">
			<view class="stats-row">
				<view class="stats-item" @click="goLogPage('all')">
					<text class="stats-num">{{ stats.totalUploads }}</text>
					<text class="stats-label">上传次数</text>
				</view>
				<view class="stats-item stats-ok" @click="goLogPage('success')">
					<text class="stats-num">{{ stats.successCount }}</text>
					<text class="stats-label">成功</text>
				</view>
				<view class="stats-item stats-fail" @click="goLogPage('fail')">
					<text class="stats-num">{{ stats.failCount }}</text>
					<text class="stats-label">失败</text>
				</view>
				<view class="stats-item">
					<text class="stats-num">{{ stats.skipCount }}</text>
					<text class="stats-label">跳过</text>
				</view>
				<view class="stats-item">
					<text class="stats-num">{{ stats.totalSmsCount }}</text>
					<text class="stats-label">短信条数</text>
				</view>
				<view class="stats-item">
					<text class="stats-num">{{ stats.totalCallCount }}</text>
					<text class="stats-label">来电条数</text>
				</view>
			</view>
			<view class="stats-footer">
				<text class="stats-hint" v-if="stats.lastSuccessTime">最近成功 {{ stats.lastSuccessTime }}</text>
				<text class="stats-hint" v-else>暂无上传记录</text>
				<view class="stats-actions">
					<text class="stats-reset" @click="clearActiveList">清空当前列表</text>
					<text class="stats-reset" @click="resetStats">重置统计</text>
					<text class="stats-reset" @click="manualFetch">手动刷新</text>
				</view>
			</view>
		</view>

		<view class="status-row">
			<text class="summary" v-if="smsList.length || callList.length">
				短信 {{ smsList.length }} 条 / 来电 {{ callList.length }} 条
			</text>
			<text class="empty" v-else>{{ activeEmptyText }}</text>
			<text class="upload-status" v-if="lastUploadTime">
				（上次上传 {{ lastUploadTime }} {{ lastUploadOk ? '成功' : '失败' }}）
			</text>
			<text class="sms-warning" v-if="smsList.length > 400">短信已超过 400 条，建议手动清理部分短信</text>
		</view>

		<view class="record-tabs">
			<view
				:class="['record-tab', activeRecordType === 'sms' ? 'record-tab-active' : '']"
				@click="activeRecordType = 'sms'"
			>
				短信 ({{ smsList.length }})
			</view>
			<view
				:class="['record-tab', activeRecordType === 'call' ? 'record-tab-active' : '']"
				@click="activeRecordType = 'call'"
			>
				来电 ({{ callList.length }})
			</view>
		</view>

		<scroll-view class="record-list" scroll-y v-if="activeList.length">
			<view class="record-item" v-for="(item, index) in activeList" :key="item.id || index">
				<view class="record-header">
					<text class="record-phone">{{ item.phone || '未知号码' }}</text>
					<text class="record-type">
						{{ activeRecordType === 'sms' ? getSmsTypeText(item.type) : item.typeText }}
					</text>
				</view>
				<text class="record-time">{{ item.time || '' }}</text>
				<text class="record-meta" v-if="activeRecordType === 'call'">
					{{ item.name || '未命名联系人' }} · 通话时长 {{ formatDuration(item.duration) }}
				</text>
				<text class="record-body" v-if="activeRecordType === 'sms'">{{ item.body || '' }}</text>
				<text class="record-body" v-else>{{ item.isNew ? '未读通话记录' : '已读通话记录' }}</text>
			</view>
		</scroll-view>
	</view>
</template>

<script>
const SETTINGS_KEY = 'sms_settings';
const STATS_KEY = 'sms_upload_stats';
const LOGS_KEY = 'sms_upload_logs';
const SMS_IDS_KEY = 'sms_uploaded_ids';
const CALL_IDS_KEY = 'call_uploaded_ids';

const PERMISSIONS = {
	readSms: 'android.permission.READ_SMS',
	receiveSms: 'android.permission.RECEIVE_SMS',
	readCallLog: 'android.permission.READ_CALL_LOG',
	readPhoneState: 'android.permission.READ_PHONE_STATE'
};

const INTERVAL_OPTIONS = [
	{ label: '3 秒', value: 3 },
	{ label: '5 秒', value: 5 },
	{ label: '10 秒', value: 10 },
	{ label: '30 秒', value: 30 },
	{ label: '1 分钟', value: 60 },
	{ label: '30 分钟', value: 1800 },
	{ label: '60 分钟', value: 3600 }
];

function formatDateTime(timestamp) {
	const d = new Date(Number(timestamp) || Date.now());
	const pad = n => (n < 10 ? '0' + n : '' + n);
	return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
}

function getCursorColumn(cursor, column) {
	const index = cursor.getColumnIndex(column);
	return index >= 0 ? index : -1;
}

function getCursorString(cursor, column) {
	const index = getCursorColumn(cursor, column);
	return index >= 0 ? cursor.getString(index) || '' : '';
}

function getCursorInt(cursor, column) {
	const index = getCursorColumn(cursor, column);
	return index >= 0 ? Number(cursor.getInt(index) || 0) : 0;
}

function getCursorLong(cursor, column) {
	const index = getCursorColumn(cursor, column);
	return index >= 0 ? Number(cursor.getLong(index) || 0) : 0;
}

function getCallTypeText(type) {
	switch (Number(type)) {
		case 1:
			return '来电已接';
		case 3:
			return '来电未接';
		case 5:
			return '来电拒接';
		default:
			return '来电记录';
	}
}

function getSmsTypeText(type) {
	return Number(type) === 2 ? '已发送' : '已接收';
}

function readSmsFromDevice(limit = 1000) {
	try {
		const main = plus.android.runtimeMainActivity();
		const Uri = plus.android.importClass('android.net.Uri');
		plus.android.importClass('android.content.ContentResolver');
		plus.android.importClass('android.database.Cursor');

		const contentResolver = main.getContentResolver();
		const smsUri = Uri.parse('content://sms');
		const cursor = contentResolver.query(smsUri, null, null, null, 'date DESC');
		const list = [];

		if (cursor) {
			let count = 0;
			while (cursor.moveToNext() && count < limit) {
				const id = getCursorString(cursor, '_id');
				const phone = getCursorString(cursor, 'address');
				const body = getCursorString(cursor, 'body');
				const dateLong = getCursorLong(cursor, 'date');
				const type = getCursorInt(cursor, 'type');

				// 只保留收到的短信，过滤自己发出去的短信
				if (type === 2) {
					continue;
				}

				list.push({
					id,
					phone,
					body,
					time: formatDateTime(dateLong),
					type,
					timestamp: dateLong
				});
				count++;
			}
			cursor.close();
		}

		return { code: 200, data: list, count: list.length };
	} catch (e) {
		console.log('[SMS] readSmsFromDevice error', e);
		return { code: -1, data: [], count: 0, msg: String(e) };
	}
}

function readCallRecordsFromDevice(limit = 500) {
	try {
		const main = plus.android.runtimeMainActivity();
		const Uri = plus.android.importClass('android.net.Uri');
		plus.android.importClass('android.content.ContentResolver');
		plus.android.importClass('android.database.Cursor');

		const contentResolver = main.getContentResolver();
		const callUri = Uri.parse('content://call_log/calls');
		const cursor = contentResolver.query(callUri, null, null, null, 'date DESC');
		const list = [];
		const incomingTypes = [1, 3, 5];

		if (cursor) {
			let count = 0;
			while (cursor.moveToNext() && count < limit) {
				const type = getCursorInt(cursor, 'type');
				// 只保留打进来的号码，过滤主动呼出的号码
				if (!incomingTypes.includes(type)) {
					continue;
				}

				const id = getCursorString(cursor, '_id');
				const phone = getCursorString(cursor, 'number');
				const name = getCursorString(cursor, 'cached_name');
				const duration = getCursorInt(cursor, 'duration');
				const dateLong = getCursorLong(cursor, 'date');
				const isNew = getCursorInt(cursor, 'new') === 1;

				list.push({
					id,
					phone,
					name,
					duration,
					type,
					typeText: getCallTypeText(type),
					time: formatDateTime(dateLong),
					timestamp: dateLong,
					isNew
				});
				count++;
			}
			cursor.close();
		}

		return { code: 200, data: list, count: list.length };
	} catch (e) {
		console.log('[CALL] readCallRecordsFromDevice error', e);
		return { code: -1, data: [], count: 0, msg: String(e) };
	}
}

const KeepAliveHelper = {
	notifyId: 1001,
	channelId: 'sms_call_monitor_service',

	showNotification(title, content) {
		try {
			const main = plus.android.runtimeMainActivity();
			const Context = plus.android.importClass('android.content.Context');
			const NotificationManager = plus.android.importClass('android.app.NotificationManager');
			const nm = main.getSystemService(Context.NOTIFICATION_SERVICE);
			const Build = plus.android.importClass('android.os.Build');

			if (Build.VERSION.SDK_INT >= 26) {
				const NotificationChannel = plus.android.importClass('android.app.NotificationChannel');
				const channel = new NotificationChannel(
					this.channelId,
					'短信来电监听服务',
					NotificationManager.IMPORTANCE_LOW
				);
				channel.setDescription('后台短信和来电监听常驻通知');
				nm.createNotificationChannel(channel);
			}

			const NotificationBuilder = plus.android.importClass('android.app.Notification$Builder');
			const builder = new NotificationBuilder(main, this.channelId);
			builder.setContentTitle(title || '短信来电监听');
			builder.setContentText(content || '后台短信和来电监听服务运行中');
			builder.setSmallIcon(main.getApplicationInfo().icon);
			builder.setOngoing(true);
			nm.notify(this.notifyId, builder.build());
		} catch (e) {
			console.log('[KeepAlive] showNotification error', e);
		}
	},

	requestIgnoreBattery() {
		try {
			const main = plus.android.runtimeMainActivity();
			const Intent = plus.android.importClass('android.content.Intent');
			const Settings = plus.android.importClass('android.provider.Settings');
			const Uri = plus.android.importClass('android.net.Uri');
			const PowerManager = plus.android.importClass('android.os.PowerManager');
			const Context = plus.android.importClass('android.content.Context');
			const pm = main.getSystemService(Context.POWER_SERVICE);

			if (!pm.isIgnoringBatteryOptimizations(main.getPackageName())) {
				const intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
				intent.setData(Uri.parse('package:' + main.getPackageName()));
				main.startActivity(intent);
			}
		} catch (e) {
			console.log('[KeepAlive] requestIgnoreBattery error', e);
		}
	}
};

export default {
	data() {
		return {
			title: '短信来电监听',
			loading: false,
			smsEmptyText: '点击手动刷新或开启轮询',
			callEmptyText: '点击手动刷新或开启轮询',
			smsList: [],
			callList: [],
			activeRecordType: 'sms',
			apiUrl: 'https://test-server.sbnews.net/api/sms/uploadSms',
			intervalIndex: 2,
			polling: false,
			countdown: 0,
			nativeTimer: null,
			countdownTimer: null,
			lastUploadTime: '',
			lastUploadOk: false,
			intervalLabels: INTERVAL_OPTIONS.map(item => item.label),
			showApiModal: false,
			apiUrlTemp: '',
			permissionStatus: '点击获取',
			stats: {
				totalUploads: 0,
				successCount: 0,
				failCount: 0,
				skipCount: 0,
				totalSmsCount: 0,
				totalCallCount: 0,
				lastSuccessTime: ''
			}
		};
	},
	computed: {
		intervalSeconds() {
			return INTERVAL_OPTIONS[this.intervalIndex].value;
		},
		countdownText() {
			if (this.loading) return '读取中...';
			if (this.countdown <= 0) return '等待中...';
			if (this.countdown >= 60) {
				const m = Math.floor(this.countdown / 60);
				const s = this.countdown % 60;
				return `${m}分${s < 10 ? '0' : ''}${s}秒后刷新`;
			}
			return `${this.countdown}秒后刷新`;
		},
		activeList() {
			return this.activeRecordType === 'sms' ? this.smsList : this.callList;
		},
		activeEmptyText() {
			return this.activeRecordType === 'sms' ? this.smsEmptyText : this.callEmptyText;
		}
	},
	onLoad() {
		this.loadSettings();
		this.refreshPermissionStatus();
		if (this.isAppAndroid()) {
			KeepAliveHelper.showNotification('短信来电监听', '后台短信和来电监听服务运行中');
			KeepAliveHelper.requestIgnoreBattery();
		}
	},
	onUnload() {
		this.stopPolling();
	},
	onShow() {
		this.refreshPermissionStatus();
		if (!this._initFetched) {
			this._initFetched = true;
			this.doFetchAndUpload();
		}
	},
	methods: {
		getSmsTypeText,
		isAppAndroid() {
			return typeof plus !== 'undefined' && !!plus.android && uni.getSystemInfoSync().platform === 'android';
		},
		formatDuration(seconds) {
			const total = Number(seconds) || 0;
			if (total < 60) return `${total} 秒`;
			const m = Math.floor(total / 60);
			const s = total % 60;
			return `${m} 分 ${s} 秒`;
		},
		getStorageKey(type) {
			return type === 'sms' ? SMS_IDS_KEY : CALL_IDS_KEY;
		},
		getApiUrl(type) {
			return this.apiUrl;
		},
		getRecordLabel(type) {
			return type === 'sms' ? '短信' : '来电';
		},
		logUploadRequest(type, payload, apiUrl, deviceId) {
			console.log(`[UPLOAD][${type}] request`, JSON.stringify({
				apiUrl,
				deviceId,
				payload
			}));
		},
		logUploadResponse(type, res, treatAsSkip = false) {
			console.log(`[UPLOAD][${type}] response`, JSON.stringify({
				statusCode: res?.statusCode || 0,
				treatAsSkip,
				data: res?.data || null
			}));
		},
		logUploadFail(type, err, payload, apiUrl, deviceId) {
			console.log(`[UPLOAD][${type}] fail`, JSON.stringify({
				apiUrl,
				deviceId,
				payload,
				error: err || null
			}));
		},
		getPayload(type, list, deviceId) {
			if (type === 'sms') {
				return { smsList: list, timestamp: Date.now(), deviceId };
			}
			return { callList: list, timestamp: Date.now(), deviceId };
		},
		loadSettings() {
			const saved = uni.getStorageSync(SETTINGS_KEY);
			if (saved) {
				this.apiUrl = saved.apiUrl || this.apiUrl;
				this.intervalIndex = saved.intervalIndex ?? 2;
			}

			const stats = uni.getStorageSync(STATS_KEY);
			if (stats) {
				this.stats = { ...this.stats, ...stats };
			}

			if (!this.stats.totalCallCount) {
				const uploadedCallIds = uni.getStorageSync(CALL_IDS_KEY) || [];
				this.stats.totalCallCount = uploadedCallIds.length;
			}
		},
		saveSettings() {
			uni.setStorageSync(SETTINGS_KEY, {
				apiUrl: this.apiUrl,
				intervalIndex: this.intervalIndex
			});
		},
		showApiUrlDialog() {
			this.apiUrlTemp = this.apiUrl;
			this.showApiModal = true;
		},
		confirmApiUrl() {
			this.apiUrl = this.apiUrlTemp.trim();
			this.showApiModal = false;
			this.saveSettings();
		},
		onIntervalChange(e) {
			this.intervalIndex = Number(e.detail.value);
			this.saveSettings();
			if (this.polling) {
				this.countdown = this.intervalSeconds;
				this.stopNativeTimer();
				this.stopCountdownTick();
				this.startNativeTimer();
				this.startCountdownTick();
			}
		},
		async onSwitchChange(e) {
			if (e.detail.value) {
				const authorized = await this.ensurePermissions();
				if (!authorized) {
					this.$nextTick(() => {
						this.polling = false;
					});
					uni.showToast({ title: '请先授予短信和通话记录权限', icon: 'none' });
					return;
				}
				this.startPolling();
				return;
			}
			this.stopPolling();
		},
		startPolling() {
			this.polling = true;
			this.countdown = this.intervalSeconds;
			this.doFetchAndUpload();
			this.startNativeTimer();
			this.startCountdownTick();
		},
		stopPolling() {
			this.polling = false;
			this.stopNativeTimer();
			this.stopCountdownTick();
		},
		startNativeTimer() {
			if (!this.isAppAndroid()) return;
			this.stopNativeTimer();
			const interval = this.intervalSeconds * 1000;
			const Timer = plus.android.importClass('java.util.Timer');
			const timer = new Timer();
			const that = this;
			const task = plus.android.implements('java.util.TimerTask', {
				run() {
					that.countdown = that.intervalSeconds;
					that.doFetchAndUpload();
				}
			});
			timer.schedule(task, interval, interval);
			this.nativeTimer = timer;
		},
		stopNativeTimer() {
			if (!this.nativeTimer) return;
			try {
				this.nativeTimer.cancel();
			} catch (e) {
				console.log('[Timer] stopNativeTimer error', e);
			}
			this.nativeTimer = null;
		},
		startCountdownTick() {
			this.stopCountdownTick();
			this.countdownTimer = setInterval(() => {
				if (this.countdown > 0) {
					this.countdown--;
				}
				if (this.countdown <= 0 && this.polling) {
					this.countdown = this.intervalSeconds;
					this.doFetchAndUpload();
				}
			}, 1000);
		},
		stopCountdownTick() {
			if (this.countdownTimer) {
				clearInterval(this.countdownTimer);
				this.countdownTimer = null;
			}
			this.countdown = 0;
		},
		checkNativePermission(perm) {
			try {
				const main = plus.android.runtimeMainActivity();
				const PackageManager = plus.android.importClass('android.content.pm.PackageManager');
				return main.checkSelfPermission(perm) === PackageManager.PERMISSION_GRANTED;
			} catch (e) {
				return false;
			}
		},
		ensurePermissions() {
			return new Promise(resolve => {
				// #ifdef APP-PLUS
				const platform = uni.getSystemInfoSync().platform;
				if (platform !== 'android') {
					resolve(true);
					return;
				}

				const hasSmsPermission = this.checkNativePermission(PERMISSIONS.readSms);
				const hasCallPermission = this.checkNativePermission(PERMISSIONS.readCallLog);
				if (hasSmsPermission && hasCallPermission) {
					this.permissionStatus = '已授权';
					resolve(true);
					return;
				}

				plus.android.requestPermissions(
					[
						PERMISSIONS.readSms,
						PERMISSIONS.receiveSms,
						PERMISSIONS.readCallLog,
						PERMISSIONS.readPhoneState
					],
					result => {
						this.refreshPermissionStatus();
						const grantedSms = this.checkNativePermission(PERMISSIONS.readSms);
						const grantedCall = this.checkNativePermission(PERMISSIONS.readCallLog);
						if (grantedSms && grantedCall) {
							resolve(true);
							return;
						}

						console.log('[Permission] request result', JSON.stringify(result));
						uni.showModal({
							title: '权限提示',
							content: '短信或通话记录权限被拒绝，请到系统设置中手动开启。',
							confirmText: '去设置',
							cancelText: '取消',
							success: res => {
								if (res.confirm) {
									this.openAppSettings();
								}
								resolve(false);
							}
						});
					},
					error => {
						console.log('[Permission] request error', error);
						resolve(false);
					}
				);
				// #endif
				// #ifndef APP-PLUS
				resolve(true);
				// #endif
			});
		},
		openAppSettings() {
			try {
				const main = plus.android.runtimeMainActivity();
				const Intent = plus.android.importClass('android.content.Intent');
				const Settings = plus.android.importClass('android.provider.Settings');
				const Uri = plus.android.importClass('android.net.Uri');
				const intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
				intent.setData(Uri.parse('package:' + main.getPackageName()));
				main.startActivity(intent);
			} catch (e) {
				console.log('[Permission] openAppSettings error', e);
			}
		},
		refreshPermissionStatus() {
			try {
				// #ifdef APP-PLUS
				const platform = uni.getSystemInfoSync().platform;
				if (platform !== 'android') {
					this.permissionStatus = '已授权';
					return;
				}
				const hasSmsPermission = this.checkNativePermission(PERMISSIONS.readSms);
				const hasCallPermission = this.checkNativePermission(PERMISSIONS.readCallLog);
				if (hasSmsPermission && hasCallPermission) {
					this.permissionStatus = '已授权';
				} else if (hasSmsPermission || hasCallPermission) {
					this.permissionStatus = '部分授权';
				} else {
					this.permissionStatus = '未授权';
				}
				// #endif
			} catch (e) {
				this.permissionStatus = '点击获取';
			}
		},
		async manualFetch() {
			if (this.loading) return;
			const authorized = await this.ensurePermissions();
			if (!authorized) return;
			await this.doFetchAndUpload();
			if (this.polling) {
				this.countdown = this.intervalSeconds;
			}
		},
		async doFetchAndUpload() {
			if (this.loading) return;
			this.loading = true;

			try {
				const smsData = readSmsFromDevice(1000);
				this.handleFetchedRecords('sms', smsData, '暂无短信');

				const callData = readCallRecordsFromDevice(500);
				this.handleFetchedRecords('call', callData, '暂无来电记录');
			} catch (error) {
				console.log('[Monitor] doFetchAndUpload error', error);
				this.smsEmptyText = '读取短信异常: ' + String(error);
				this.callEmptyText = '读取来电异常: ' + String(error);
			} finally {
				this.loading = false;
			}
		},
		handleFetchedRecords(type, result, emptyText) {
			const listKey = type === 'sms' ? 'smsList' : 'callList';
			const emptyKey = type === 'sms' ? 'smsEmptyText' : 'callEmptyText';

			if (result.code !== 200) {
				this[listKey] = [];
				this[emptyKey] = result.msg || '读取失败';
				return;
			}

			const list = result.data || [];
			this[listKey] = list;
			this[emptyKey] = list.length ? '' : emptyText;

			const uploadedIds = new Set(uni.getStorageSync(this.getStorageKey(type)) || []);
			const newList = list.filter(item => !uploadedIds.has(String(item.id)));

			if (newList.length) {
				this.uploadToApi(type, newList);
			} else if (list.length) {
				this.stats.skipCount++;
				uni.setStorageSync(STATS_KEY, this.stats);
			}
		},
		uploadToApi(type, list) {
			const apiUrl = this.getApiUrl(type);
			if (!apiUrl) {
				console.log(`[${type}] apiUrl missing, skip upload`);
				return;
			}

			let deviceId = '';
			try {
				const sysInfo = uni.getSystemInfoSync();
				deviceId = sysInfo.deviceId || '';
			} catch (e) {}

			const payload = this.getPayload(type, list, deviceId);
			this.logUploadRequest(type, payload, apiUrl, deviceId);
			uni.request({
				url: apiUrl,
				method: 'POST',
				header: { 'content-type': 'application/json' },
				data: payload,
				success: res => {
					const ok = res.statusCode === 200 && res.data && res.data.code === 1;
					this.logUploadResponse(type, res, false);
					this.lastUploadOk = ok;
					this.lastUploadTime = this.formatNow();
					const msg = res.data?.msg || (ok ? '上传成功' : '服务端返回异常');
					this.updateStats(type, ok, list, {
						statusCode: res.statusCode,
						message: msg,
						deviceId,
						apiUrl,
						requestData: JSON.stringify(payload),
						responseData: JSON.stringify(res.data)
					});
				},
				fail: err => {
					this.logUploadFail(type, err, payload, apiUrl, deviceId);
					this.lastUploadOk = false;
					this.lastUploadTime = this.formatNow();
					this.updateStats(type, false, [], {
						statusCode: 0,
						message: String(err.errMsg || err),
						deviceId,
						apiUrl,
						requestData: JSON.stringify(payload),
						responseData: JSON.stringify(err)
					});
				}
			});
		},
		clearActiveList() {
			if (this.activeRecordType === 'sms') {
				this.smsList = [];
				this.smsEmptyText = '已清空，等待下次轮询';
			} else {
				this.callList = [];
				this.callEmptyText = '已清空，等待下次轮询';
			}
			this.lastUploadTime = '';
		},
		updateStats(type, ok, list, extra) {
			this.stats.totalUploads++;
			const logEntry = {
				ok,
				time: this.formatFull(),
				recordType: type,
				recordTypeLabel: this.getRecordLabel(type),
				recordCount: list.length,
				smsCount: list.length,
				statusCode: extra?.statusCode || '',
				message: extra?.message || '',
				apiUrl: extra?.apiUrl || this.getApiUrl(type),
				requestData: extra?.requestData || '',
				responseData: extra?.responseData || ''
			};

			if (ok && list.length) {
				this.stats.successCount++;
				this.stats.lastSuccessTime = this.formatNow();
				const ids = uni.getStorageSync(this.getStorageKey(type)) || [];
				const idSet = new Set(ids);
				list.forEach(item => {
					if (item.id) {
						idSet.add(String(item.id));
					}
				});
				const newIds = Array.from(idSet);
				uni.setStorageSync(this.getStorageKey(type), newIds);
				if (type === 'sms') {
					this.stats.totalSmsCount = newIds.length;
				} else {
					this.stats.totalCallCount = newIds.length;
				}
			} else if (!ok) {
				this.stats.failCount++;
			}

			const logs = uni.getStorageSync(LOGS_KEY) || [];
			logs.unshift(logEntry);
			if (logs.length > 200) logs.length = 200;

			uni.setStorageSync(STATS_KEY, this.stats);
			uni.setStorageSync(LOGS_KEY, logs);
		},
		resetStats() {
			this.stats = {
				totalUploads: 0,
				successCount: 0,
				failCount: 0,
				skipCount: 0,
				totalSmsCount: 0,
				totalCallCount: 0,
				lastSuccessTime: ''
			};
			uni.setStorageSync(STATS_KEY, this.stats);
			uni.removeStorageSync(SMS_IDS_KEY);
			uni.removeStorageSync(CALL_IDS_KEY);
			uni.removeStorageSync(LOGS_KEY);
			uni.showToast({ title: '统计已重置', icon: 'none' });
		},
		goLogPage(tab) {
			uni.navigateTo({ url: `/pages/logs/logs?tab=${tab}` });
		},
		formatFull() {
			return formatDateTime(Date.now());
		},
		formatNow() {
			const d = new Date();
			const pad = n => (n < 10 ? '0' + n : '' + n);
			return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
		}
	}
};
</script>

<style>
.content {
	display: flex;
	flex-direction: column;
	align-items: center;
	min-height: 100vh;
	padding: 40rpx 32rpx 32rpx;
	background-color: #f6f7fb;
	box-sizing: border-box;
}

.panel-card,
.stats-card,
.record-tabs,
.record-list {
	width: 100%;
	box-sizing: border-box;
}

.panel-card,
.stats-card {
	padding: 24rpx;
	margin-bottom: 24rpx;
	background-color: #ffffff;
	border-radius: 20rpx;
	box-shadow: 0 4rpx 16rpx rgba(15, 23, 42, 0.06);
}

.panel-grid {
	display: flex;
	flex-wrap: nowrap;
	gap: 16rpx;
}

.panel-cell {
	flex: 1 1 0;
	min-width: 0;
	display: flex;
	flex-direction: column;
	align-items: center;
	padding: 20rpx 12rpx;
	background-color: #f8fafc;
	border-radius: 16rpx;
	position: relative;
}

.panel-cell:active {
	background-color: #eef2ff;
}

.cell-inner {
	display: flex;
	flex-direction: column;
	align-items: center;
	width: 100%;
}

.cell-icon {
	font-size: 30rpx;
	font-weight: 600;
	margin-bottom: 8rpx;
	color: #1d4ed8;
}

.cell-label {
	font-size: 24rpx;
	color: #374151;
	font-weight: 500;
	margin-bottom: 4rpx;
}

.cell-desc {
	font-size: 20rpx;
	color: #9ca3af;
}

.panel-switches {
	display: flex;
	justify-content: space-between;
	align-items: center;
	margin-top: 20rpx;
	padding-top: 20rpx;
	border-top: 1rpx solid #f0f0f0;
}

.switch-item {
	display: flex;
	align-items: center;
}

.switch-left {
	flex: 0 0 auto;
}

.switch-right {
	flex: 1;
	justify-content: flex-end;
}

.switch-label,
.countdown-text {
	font-size: 26rpx;
}

.countdown-text {
	font-weight: 600;
	color: #2563eb;
}

.stats-row {
	display: flex;
	flex-wrap: wrap;
}

.stats-item {
	width: 33.33%;
	display: flex;
	flex-direction: column;
	align-items: center;
	padding: 12rpx 0;
}

.stats-num {
	font-size: 36rpx;
	font-weight: 700;
	color: #1f2937;
}

.stats-ok .stats-num {
	color: #16a34a;
}

.stats-fail .stats-num {
	color: #dc2626;
}

.stats-label {
	margin-top: 4rpx;
	font-size: 22rpx;
	color: #9ca3af;
}

.stats-footer {
	display: flex;
	justify-content: space-between;
	align-items: center;
	margin-top: 16rpx;
	padding-top: 16rpx;
	border-top: 1rpx solid #f0f0f0;
}

.stats-hint {
	font-size: 22rpx;
	color: #9ca3af;
}

.stats-actions {
	display: flex;
	gap: 12rpx;
}

.stats-reset {
	font-size: 22rpx;
	color: #ef4444;
	padding: 4rpx 16rpx;
	border: 1rpx solid #fca5a5;
	border-radius: 8rpx;
	background-color: #fef2f2;
}

.status-row {
	width: 100%;
	margin-bottom: 20rpx;
	display: flex;
	flex-direction: column;
	gap: 8rpx;
}

.summary,
.empty {
	font-size: 26rpx;
	color: #374151;
}

.upload-status {
	font-size: 22rpx;
	color: #6b7280;
}

.sms-warning {
	font-size: 24rpx;
	color: #d97706;
}

.record-tabs {
	display: flex;
	margin-bottom: 16rpx;
	padding: 8rpx;
	background: #ffffff;
	border-radius: 20rpx;
	box-shadow: 0 4rpx 16rpx rgba(15, 23, 42, 0.06);
}

.record-tab {
	flex: 1;
	text-align: center;
	padding: 18rpx 0;
	font-size: 26rpx;
	color: #6b7280;
	border-radius: 14rpx;
}

.record-tab-active {
	color: #1d4ed8;
	font-weight: 600;
	background-color: #eff6ff;
}

.record-list {
	flex: 1;
	min-height: 360rpx;
}

.record-item {
	margin-bottom: 16rpx;
	padding: 24rpx;
	background-color: #ffffff;
	border-radius: 16rpx;
	box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
}

.record-header {
	display: flex;
	justify-content: space-between;
	align-items: center;
	gap: 16rpx;
}

.record-phone {
	flex: 1;
	font-size: 28rpx;
	font-weight: 600;
	color: #111827;
	word-break: break-all;
}

.record-type {
	font-size: 22rpx;
	color: #2563eb;
	background-color: #eff6ff;
	padding: 4rpx 12rpx;
	border-radius: 999rpx;
}

.record-time,
.record-meta {
	display: block;
	margin-top: 10rpx;
	font-size: 22rpx;
	color: #6b7280;
}

.record-body {
	display: block;
	margin-top: 12rpx;
	font-size: 24rpx;
	line-height: 1.6;
	color: #374151;
	word-break: break-all;
}

.modal-mask {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	background-color: rgba(0, 0, 0, 0.5);
	display: flex;
	align-items: center;
	justify-content: center;
	z-index: 999;
}

.modal-box {
	width: 620rpx;
	padding: 40rpx;
	background-color: #ffffff;
	border-radius: 24rpx;
}

.modal-title {
	display: block;
	margin-bottom: 24rpx;
	text-align: center;
	font-size: 32rpx;
	font-weight: 600;
	color: #1f2937;
}

.modal-input {
	width: 100%;
	min-height: 120rpx;
	margin-bottom: 24rpx;
	padding: 16rpx;
	font-size: 26rpx;
	line-height: 1.6;
	border: 1rpx solid #e5e7eb;
	border-radius: 12rpx;
	box-sizing: border-box;
	word-break: break-all;
}

.modal-btns {
	display: flex;
	gap: 16rpx;
}

.modal-btn {
	flex: 1;
}
</style>
