<template>
	<view class="content">
		<view class="navbox">
			<view class="status_bar"></view>
			<uni-nav-bar
				left-icon="left"
				title="患者列表压测"
				@clickLeft="goBack"
				color="#8B2316"
				:border="false"
				backgroundColor="#eee"
			>
				<template #right>
					<view class="nav-right">
						<uni-icons type="search" size="24" color="#8B2316"></uni-icons>
					</view>
				</template>
			</uni-nav-bar>
		</view>

		<view class="patient-list">
			<view class="perf-toolbar">
				<view class="perf-btn primary" @click="loadRealData">加载真实数据</view>
				<view class="perf-btn" @click="loadRealData(true)">强制刷新</view>
				<view class="perf-btn" @click="clearData">清空</view>
			</view>
			<view class="perf-tip">当前数据量: {{ patientList.length }}，分组: {{ patientGroups.length }}，当前字母: {{ activeLetter || '-' }}</view>

			<view class="listbox patient-building" v-if="patientDataLoading && !loadFinish">
				<text class="patient-building-text">正在生成并整理模拟数据...</text>
			</view>

			<view class="listbox" v-show="patientList.length > 0">
				<scroll-view
					class="groups-scroll"
					scroll-y="true"
					:scroll-into-view="scrollIntoViewId"
					:scroll-with-animation="true"
					@scroll="onGroupsScroll"
					v-show="loadFinish"
				>
					<view class="special-actions">
						<view class="action-item">
							<view class="action-icon new-patient"></view>
							<text class="action-text">新的患者(模拟)</text>
							<uni-icons type="right" size="20" color="#999"></uni-icons>
						</view>
					</view>

					<view class="group-section" v-for="group in patientGroups" :key="group.letter" :id="`anchor-${group.letter}`">
						<view class="group-header">{{ group.letter }}</view>
						<view class="patient-item" v-for="item in group.items" :key="item.uuid || item.id">
							<view class="patient-avatar avatar-placeholder"></view>
							<view class="patient-info">
								<text class="patient-name">{{ item.nickname || item.realName }}</text>
							</view>
							<view class="patient-status">
								<text class="follow-date">随访于{{ item.joinDateYMD }}</text>
							</view>
						</view>
					</view>
				</scroll-view>

				<view class="letter-index" v-if="loadFinish && indexList.length > 0">
					<view class="letter-item" v-for="letter in indexList" :key="letter" :class="{ active: activeLetter === letter }" @click="onLetterTap(letter)">
						{{ letter }}
					</view>
				</view>
			</view>

			<view class="listbox" v-show="patientList.length === 0 && !patientDataLoading">
				<empty></empty>
			</view>
		</view>
	</view>
</template>

<script setup>
import { nextTick, ref, shallowRef } from 'vue';
import { onBackPress } from '@dcloudio/uni-app';
import dayjs from 'dayjs';
import pinyin from 'pinyin';
import api from '@/api/api.js';

const patientList = ref([]);
const patientDataLoading = ref(false);
const loadFinish = ref(false);

const firstLetterCache = new Map();
const LETTER_ORDER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
const GROUP_BUILD_CHUNK_SIZE = 180;
const indexList = ref([]);
const patientGroups = shallowRef([]);
const allPatientGroups = shallowRef([]);
const activeLetter = ref('');
const scrollIntoViewId = ref('');
const groupScrollRanges = ref([]);

const sortLetter = (a, b) => {
	if (a === '#') return 1;
	if (b === '#') return -1;
	const ai = LETTER_ORDER.indexOf(a);
	const bi = LETTER_ORDER.indexOf(b);
	if (ai === -1 && bi === -1) return String(a).localeCompare(String(b));
	if (ai === -1) return 1;
	if (bi === -1) return -1;
	return ai - bi;
};

const formatYMD = (input) => {
	if (!input) return '';
	const d = dayjs(input);
	return d.isValid() ? d.format('YYYY-MM-DD') : '';
};

const getFirstLetter = (name = '') => {
	const firstChar = String(name).trim().charAt(0);
	if (!firstChar) return '#';
	if (/^[A-Za-z]$/.test(firstChar)) return firstChar.toUpperCase();
	if (firstLetterCache.has(firstChar)) return firstLetterCache.get(firstChar);
	try {
		const py = pinyin(firstChar, { style: pinyin.STYLE_NORMAL })?.[0]?.[0] || '';
		const letter = py ? py.charAt(0).toUpperCase() : '#';
		firstLetterCache.set(firstChar, letter);
		return letter;
	} catch (error) {
		return '#';
	}
};

const rebuildIndexList = () => {
	indexList.value = allPatientGroups.value
		.map(g => String(g?.letter || '').toUpperCase())
		.filter((letter, index, arr) => (letter === '#' || /^[A-Z]$/.test(letter)) && arr.indexOf(letter) === index)
		.sort(sortLetter);
	activeLetter.value = indexList.value[0] || '';
};
const onLetterTap = (letter) => {
	if (!letter) return;
	activeLetter.value = letter;
	const targetId = `anchor-${letter}`;
	if (scrollIntoViewId.value === targetId) {
		scrollIntoViewId.value = '';
		nextTick(() => {
			scrollIntoViewId.value = targetId;
		});
		return;
	}
	scrollIntoViewId.value = targetId;
};
const rpxToPx = (rpx) => {
	const { screenWidth } = uni.getSystemInfoSync();
	return Math.round((screenWidth / 750) * rpx);
};
const buildGroupScrollRanges = () => {
	const actionHeight = rpxToPx(120); // action-item 高度近似
	const headerHeight = rpxToPx(80); // group-header 高度近似
	const itemHeight = rpxToPx(150); // patient-item 高度近似
	let cursor = actionHeight;
	groupScrollRanges.value = patientGroups.value.map(group => {
		const groupHeight = headerHeight + (group.items?.length || 0) * itemHeight;
		const range = {
			letter: group.letter,
			start: cursor,
			end: cursor + groupHeight
		};
		cursor += groupHeight;
		return range;
	});
};
const onGroupsScroll = (e) => {
	const top = Number(e?.detail?.scrollTop || 0);
	if (!groupScrollRanges.value.length) return;
	let current = groupScrollRanges.value[0].letter;
	for (let i = 0; i < groupScrollRanges.value.length; i++) {
		const range = groupScrollRanges.value[i];
		if (top >= range.start) {
			current = range.letter;
		} else {
			break;
		}
	}
	activeLetter.value = current;
};

const waitForNextFrame = () => new Promise(resolve => setTimeout(resolve, 0));
const buildGroupsFromPatients = async () => {
	const map = new Map();
	const source = patientList.value || [];
	for (let i = 0; i < source.length; i++) {
		const p = source[i] || {};
		const normalized = {
			...p,
			joinDateYMD: formatYMD(p.join_date)
		};
		const first = getFirstLetter(normalized.nickname || normalized.realName);
		const letter = /^[A-Z]$/.test(first) ? first : '#';
		if (!map.has(letter)) map.set(letter, []);
		map.get(letter).push(normalized);
		if (i > 0 && i % GROUP_BUILD_CHUNK_SIZE === 0) {
			await waitForNextFrame();
		}
	}
	const letters = Array.from(map.keys()).sort(sortLetter);
	allPatientGroups.value = letters.map(l => ({ letter: l, items: map.get(l) }));
	patientGroups.value = allPatientGroups.value;
	rebuildIndexList();
	buildGroupScrollRanges();
};
const loadRealData = async (force = false) => {
	if (patientDataLoading.value) return;
	if (!force && patientList.value.length > 0) return;
	patientDataLoading.value = true;
	loadFinish.value = false;
	try {
		const t1 = Date.now();
		const res = await api.patientListByGBK();
		if (res?.code !== 1) {
			uni.showToast({ title: '获取数据失败', icon: 'none' });
			return;
		}
		patientList.value = res.data || [];
		await buildGroupsFromPatients();
		loadFinish.value = true;
		const t2 = Date.now();
		uni.showToast({
			title: `加载完成 ${patientList.value.length}条/${t2 - t1}ms`,
			icon: 'none',
			duration: 1500
		});
	} finally {
		patientDataLoading.value = false;
	}
};

const clearData = () => {
	patientList.value = [];
	patientGroups.value = [];
	allPatientGroups.value = [];
	indexList.value = [];
	groupScrollRanges.value = [];
	activeLetter.value = '';
	scrollIntoViewId.value = '';
	loadFinish.value = false;
};

const goBack = () => {
	plus.runtime.quit();
};

onBackPress(() => {
	plus.runtime.quit();
	return true;
});
</script>

<style lang="scss" scoped>
.content {
	background-color: #f5f5f5;
	height: 100vh;
	overflow-y: hidden;
}
.nav-right {
	display: flex;
	align-items: center;
}
.patient-list {
	width: 100%;
	margin-top: calc(var(--status-bar-height) + 44px);
	height: calc(100vh - var(--status-bar-height) - 44px);
	overflow: hidden;
	background-color: #fff;
	display: flex;
	flex-direction: column;
}
.perf-toolbar {
	display: flex;
	align-items: center;
	padding: 16rpx 20rpx;
	border-bottom: 1rpx solid #f0f0f0;
	background: #fff;
	position: sticky;
	top: 0;
	z-index: 5;
}
.perf-btn {
	padding: 10rpx 16rpx;
	background: #f2f2f2;
	border-radius: 8rpx;
	font-size: 24rpx;
	color: #555;
	margin-right: 12rpx;
}
.perf-btn.primary {
	background: #8B2316;
	color: #fff;
}
.perf-tip {
	font-size: 24rpx;
	color: #999;
	padding: 8rpx 20rpx 14rpx;
}
.listbox {
	flex: 1;
	min-height: 0;
}
.patient-building {
	display: flex;
	align-items: center;
	justify-content: center;
}
.patient-building-text {
	font-size: 28rpx;
	color: #999;
	margin-top: 60rpx;
}
.groups-scroll {
	height: 100%;
}
.special-actions {
	background-color: #fff;
	border-bottom: 1rpx solid #f0f0f0;
}
.action-item {
	display: flex;
	align-items: center;
	padding: 30rpx;
	border-bottom: 1rpx solid #f0f0f0;
}
.action-icon {
	width: 60rpx;
	height: 60rpx;
	border-radius: 50%;
	display: flex;
	align-items: center;
	justify-content: center;
	margin-right: 20rpx;
}
.new-patient {
	background-color: #8B2316;
}
.action-text {
	flex: 1;
	font-size: 32rpx;
	color: #333;
}
.group-header {
	padding: 20rpx 30rpx;
	background-color: #e4e4e4;
	font-size: 36rpx;
	color: #666;
}
.patient-item {
	display: flex;
	align-items: center;
	padding: 30rpx 80rpx 20rpx 30rpx;
	border-bottom: 1rpx solid #f0f0f0;
	background-color: #fff;
}
.patient-avatar {
	width: 90rpx;
	height: 90rpx;
	border-radius: 10rpx;
	margin-right: 20rpx;
}
.avatar-placeholder {
	position: relative;
	background: #e0e0e0;
	border-radius: 50%;
}
.avatar-placeholder::before {
	content: '';
	position: absolute;
	width: 28rpx;
	height: 28rpx;
	border-radius: 50%;
	background: #ffffff;
	top: 16rpx;
	left: 31rpx;
}
.avatar-placeholder::after {
	content: '';
	position: absolute;
	width: 56rpx;
	height: 30rpx;
	border-radius: 28rpx 28rpx 12rpx 12rpx;
	background: #ffffff;
	left: 17rpx;
	bottom: 14rpx;
}
.patient-info {
	flex: 1;
}
.patient-name {
	font-size: 32rpx;
	color: #333;
	font-weight: 500;
}
.patient-status {
	display: flex;
	align-items: flex-end;
	flex-direction: column;
}
.follow-date {
	margin-top: 8rpx;
	font-size: 24rpx;
	color: #666;
}
.letter-index {
	position: fixed;
	right: 10rpx;
	top: 50%;
	transform: translateY(-50%);
	background-color: rgba(255, 255, 255, 0.9);
	border-radius: 20rpx;
	padding: 10rpx 5rpx;
	z-index: 999;
}
.letter-item {
	padding: 8rpx 12rpx;
	font-size: 24rpx;
	color: #666;
	text-align: center;
	border-radius: 10rpx;
}
.letter-item.active {
	color: #8B2316;
	font-weight: 600;
}
</style>
