<template>
	<view class="schedules-page">
		<scroll-view class="schedules-scroll" scroll-y :scroll-with-animation="true"
			:style="{ paddingBottom: scrollPaddingBottom + 'px' }">
			<view class="hero-panel">
				<view class="hero-main">
					<view class="hero-copy">
						<text class="hero-kicker">日程提醒</text>
						<text class="hero-title">{{ heroTitle }}</text>
						<text class="hero-desc">{{ heroDesc }}</text>
					</view>
					<view class="hero-action" @click="handleAddSchedule">
						<TnIcon name="add-lack" :size="34" color="#FFFFFF" />
					</view>
				</view>

				<view class="summary-row">
					<view class="summary-item">
						<text class="summary-value">{{ todayCount }}</text>
						<text class="summary-label">今日</text>
					</view>
					<view class="summary-divider" />
					<view class="summary-item">
						<text class="summary-value">{{ pendingCount }}</text>
						<text class="summary-label">待办</text>
					</view>
					<view class="summary-divider" />
					<view class="summary-item">
						<text class="summary-value">{{ overdueCount }}</text>
						<text class="summary-label">逾期</text>
					</view>
				</view>
			</view>

			<view class="week-strip">
				<view v-for="day in weekDays" :key="day.key" class="week-day" :class="{ 'week-day-active': day.isToday }">
					<text class="week-name">{{ day.name }}</text>
					<text class="week-date">{{ day.date }}</text>
					<view class="week-dot" :class="{ 'week-dot-on': day.count > 0 }" />
				</view>
			</view>

			<view class="tab-card">
				<TnTabs v-model="currentFilterIndex" height="78rpx" bar-width="36rpx" bar-color="#F97316"
					active-color="#F97316" color="#64748B" bg-color="#FFFFFF" font-size="25rpx"
					active-font-size="27rpx" :bottom-shadow="false">
					<TnTabsItem v-for="tab in filterTabs" :key="tab" :title="tab" />
				</TnTabs>
			</view>

			<view class="schedules-stage">
				<view v-if="loading" class="loading-state">
					<TnLoading :show="true" mode="circle" color="#F97316" :size="54" />
					<text class="loading-text">正在同步提醒...</text>
					<view v-for="i in 3" :key="i" class="skeleton-card">
						<view class="skeleton-date" />
						<view class="skeleton-content">
							<view class="skeleton-line skeleton-line-title" />
							<view class="skeleton-line skeleton-line-meta" />
						</view>
					</view>
				</view>

				<view v-else-if="filteredSchedules.length === 0" class="empty-state">
					<TnEmpty mode="data" color="#F97316" size="xl" />
					<text class="empty-title">{{ emptyTitle }}</text>
					<text class="empty-desc">{{ emptyDesc }}</text>
					<TnButton text-color="#FFFFFF" shape="round" :bold="true" :shadow="true"
						:custom-style="primaryButtonStyle" width="320" height="88" @click="handleAddSchedule">
						<view class="button-inner">
							<TnIcon name="add-lack" :size="28" />
							<text>添加提醒</text>
						</view>
					</TnButton>
				</view>

				<view v-else class="schedule-content">
					<view class="section-head">
						<view class="section-title-block">
							<text class="section-title">{{ activeFilterLabel }}</text>
							<text class="section-subtitle">{{ sectionSubtitle }}</text>
						</view>
						<TnTag :bg-color="nextTag.bg" :text-color="nextTag.color" shape="round" size="sm">
							{{ nextTag.text }}
						</TnTag>
					</view>

					<view class="schedule-list">
						<view v-for="item in filteredSchedules" :key="item._id" class="schedule-card"
							:class="{ 'schedule-card-completed': item.derivedStatus === 'completed' }"
							hover-class="schedule-card-active" @click="editSchedule(item)">
							<view class="date-chip" :class="`date-chip-${item.derivedStatus}`">
								<text class="date-month">{{ formatMonth(item.scheduledDate) }}</text>
								<text class="date-day">{{ formatDay(item.scheduledDate) }}</text>
							</view>

							<view class="schedule-main">
								<view class="title-row">
									<view class="type-icon" :style="{ backgroundColor: getTypeStyle(item.type).bg }">
										<TnIcon :name="getTypeStyle(item.type).icon" :size="30"
											:color="getTypeStyle(item.type).color" />
									</view>
									<text class="schedule-title">{{ item.title || getTypeLabel(item.type) }}</text>
								</view>

								<view class="pet-row">
									<TnAvatar :url="item.petAvatar || ''" :icon="item.petAvatar ? '' : 'floral'"
										:size="42" shape="circle" bg-color="#FFF7ED" />
									<text class="pet-name">{{ item.petName || '未关联宠物' }}</text>
									<TnTag :bg-color="getStatusStyle(item.derivedStatus).bg"
										:text-color="getStatusStyle(item.derivedStatus).color" shape="round" size="sm">
										{{ getDueText(item) }}
									</TnTag>
								</view>

								<view class="meta-row">
									<view class="meta-pill">
										<TnIcon name="calendar" :size="21" color="#64748B" />
										<text>{{ formatDate(item.scheduledDate) }}</text>
									</view>
									<view class="meta-pill">
										<TnIcon name="clock" :size="21" color="#64748B" />
										<text>{{ getFrequencyLabel(item.frequency) }}</text>
									</view>
								</view>

								<text v-if="item.note" class="schedule-note">{{ item.note }}</text>
							</view>

							<view class="action-col" @click.stop>
								<view class="icon-button" @click="toggleComplete(item)">
									<TnIcon :name="item.derivedStatus === 'completed' ? 'refresh' : 'success-circle-fill'"
										:size="30" :color="item.derivedStatus === 'completed' ? '#64748B' : '#0D9488'" />
								</view>
								<view class="icon-button danger-button" @click="deleteSchedule(item)">
									<TnIcon name="delete-fill" :size="30" color="#DC2626" />
								</view>
							</view>
						</view>
					</view>

					<view class="bottom-tip">
						<TnIcon name="success-circle-fill" :size="24" color="#14B8A6" />
						<text>已展示当前筛选下的全部提醒</text>
					</view>
				</view>
			</view>
		</scroll-view>

		<view v-if="!loading && filteredSchedules.length > 0" class="fab-wrap" :style="{ bottom: fabBottom + 'px' }">
			<TnButton text-color="#FFFFFF" shape="round" :bold="true" :shadow="true"
				:custom-style="primaryButtonStyle" width="280" height="88" :font-size="28" @click="handleAddSchedule">
				<view class="button-inner">
					<TnIcon name="add-lack" :size="28" />
					<text>添加提醒</text>
				</view>
			</TnButton>
		</view>
	</view>
</template>

<script setup lang="ts">
	import { computed, onMounted, ref } from 'vue'
	import { onShow } from '@dcloudio/uni-app'
	import TnAvatar from '@/uni_modules/tuniaoui-vue3/components/avatar/src/avatar.vue'
	import TnButton from '@/uni_modules/tuniaoui-vue3/components/button/src/button.vue'
	import TnEmpty from '@/uni_modules/tuniaoui-vue3/components/empty/src/empty.vue'
	import TnIcon from '@/uni_modules/tuniaoui-vue3/components/icon/src/icon.vue'
	import TnLoading from '@/uni_modules/tuniaoui-vue3/components/loading/src/loading.vue'
	import TnTabs from '@/uni_modules/tuniaoui-vue3/components/tabs/src/tabs.vue'
	import TnTabsItem from '@/uni_modules/tuniaoui-vue3/components/tabs/src/tabs-item.vue'
	import TnTag from '@/uni_modules/tuniaoui-vue3/components/tag/src/tag.vue'

	type ScheduleStatus = 'pending' | 'completed' | 'overdue' | 'skipped'
	type DerivedStatus = ScheduleStatus | 'today'

	interface Pet {
		_id : string
		name : string
		avatarUrl ?: string
		_displayAvatar ?: string
	}

	interface ScheduleItem {
		_id : string
		pet_id : string
		user_id : string
		type : string
		title : string
		scheduledDate : number | string
		frequency ?: string
		interval ?: number
		status ?: ScheduleStatus
		reminderDays ?: number[]
		relatedRecordId ?: string
		note ?: string
		createdAt ?: number | string
		updatedAt ?: number | string
		petName ?: string
		petAvatar ?: string
		derivedStatus : DerivedStatus
	}

	interface TypeStyle {
		icon : string
		color : string
		bg : string
	}

	const loading = ref(true)
	const hasLoaded = ref(false)
	const schedules = ref<ScheduleItem[]>([])
	const currentFilterIndex = ref(0)
	const scrollPaddingBottom = ref(118)
	const fabBottom = ref(78)
	let schedulesRequestId = 0

	const primaryButtonStyle = {
		background: 'linear-gradient(135deg, #F97316, #FB923C)',
	}

	const filterTabs = ['全部', '今日', '待办', '已逾期', '已完成']
	const filterValues = ['all', 'today', 'pending', 'overdue', 'completed']

	const typeStyleMap : Record<string, TypeStyle> = {
		vaccine: { icon: 'safe-fill', color: '#2563EB', bg: '#EFF6FF' },
		deworm: { icon: 'notice-fill', color: '#7C3AED', bg: '#F5F3FF' },
		checkup: { icon: 'data-fill', color: '#0D9488', bg: '#F0FDFA' },
		custom: { icon: 'calendar-fill', color: '#F97316', bg: '#FFF7ED' },
	}

	const statusStyleMap : Record<DerivedStatus, { color : string; bg : string }> = {
		pending: { color: '#2563EB', bg: '#EFF6FF' },
		today: { color: '#F97316', bg: '#FFF7ED' },
		overdue: { color: '#DC2626', bg: '#FEF2F2' },
		completed: { color: '#0D9488', bg: '#F0FDFA' },
		skipped: { color: '#64748B', bg: '#F8FAFC' },
	}

	const typeLabelMap : Record<string, string> = {
		vaccine: '疫苗',
		deworm: '驱虫',
		checkup: '体检',
		custom: '自定义',
	}

	const frequencyLabelMap : Record<string, string> = {
		once: '单次',
		daily: '每天',
		weekly: '每周',
		monthly: '每月',
		yearly: '每年',
	}

	const activeFilterLabel = computed(() => filterTabs[currentFilterIndex.value] || '全部')

	const filteredSchedules = computed(() => {
		const filter = filterValues[currentFilterIndex.value] || 'all'
		if (filter === 'all') return schedules.value
		if (filter === 'pending') {
			return schedules.value.filter((item) => item.derivedStatus === 'pending' || item.derivedStatus === 'today')
		}
		return schedules.value.filter((item) => item.derivedStatus === filter)
	})

	const todayCount = computed(() => schedules.value.filter((item) => item.derivedStatus === 'today').length)
	const pendingCount = computed(() => schedules.value.filter((item) => item.derivedStatus === 'pending' || item.derivedStatus === 'today').length)
	const overdueCount = computed(() => schedules.value.filter((item) => item.derivedStatus === 'overdue').length)

	const nextSchedule = computed(() => schedules.value.find((item) => item.derivedStatus === 'today' || item.derivedStatus === 'pending'))

	const heroTitle = computed(() => {
		if (loading.value) return '正在整理提醒'
		if (nextSchedule.value) return `${getDueText(nextSchedule.value)} · ${nextSchedule.value.title || getTypeLabel(nextSchedule.value.type)}`
		if (schedules.value.length) return '所有提醒都处理好了'
		return '从第一条提醒开始'
	})

	const heroDesc = computed(() => {
		if (loading.value) return '同步疫苗、驱虫、体检和自定义日程'
		if (overdueCount.value > 0) return `有 ${overdueCount.value} 条提醒已经逾期，建议优先处理。`
		if (nextSchedule.value) return `${nextSchedule.value.petName || '毛球'} · ${formatDate(nextSchedule.value.scheduledDate)}`
		return '添加下一次疫苗、驱虫或体检提醒，照护节奏会更清楚。'
	})

	const sectionSubtitle = computed(() => `${filteredSchedules.value.length} 条提醒 · 共 ${schedules.value.length} 条日程`)

	const emptyTitle = computed(() => {
		if (currentFilterIndex.value === 0) return '还没有日程提醒'
		return `暂无${activeFilterLabel.value}提醒`
	})

	const emptyDesc = computed(() => {
		if (currentFilterIndex.value === 0) return '把下一次疫苗、驱虫、复查或体检时间记下来，到点就不会慌。'
		return '切换其他状态看看，或添加一条新的照护提醒。'
	})

	const nextTag = computed(() => {
		if (loading.value) return { text: '同步中', color: '#F97316', bg: '#FFF7ED' }
		if (nextSchedule.value) return { text: getDueText(nextSchedule.value), ...getStatusStyle(nextSchedule.value.derivedStatus) }
		return { text: '已清空', color: '#0D9488', bg: '#F0FDFA' }
	})

	const weekDays = computed(() => {
		const now = new Date()
		const start = addDays(startOfDay(now), -3)
		const names = ['日', '一', '二', '三', '四', '五', '六']
		return Array.from({ length: 7 }).map((_, index) => {
			const date = addDays(start, index)
			const count = schedules.value.filter((item) => isSameDay(getDate(item.scheduledDate), date)).length
			return {
				key: formatDateKey(date),
				name: names[date.getDay()],
				date: String(date.getDate()).padStart(2, '0'),
				isToday: isSameDay(date, now),
				count,
			}
		})
	})

	onMounted(() => {
		calcLayout()
		loadSchedules()
	})

	onShow(() => {
		loadSchedules(!hasLoaded.value)
	})

	function calcLayout() {
		const systemInfo = uni.getSystemInfoSync()
		const safeBottom = systemInfo.safeAreaInsets?.bottom || 0
		fabBottom.value = safeBottom + 58
		scrollPaddingBottom.value = safeBottom + 118
	}

	async function loadSchedules(showLoading = !hasLoaded.value) {
		const requestId = ++schedulesRequestId
		if (showLoading) loading.value = true
		try {
			const uid = uni.getStorageSync('uid')
			if (!uid) {
				if (requestId !== schedulesRequestId) return
				schedules.value = []
				return
			}

			const [petList, scheduleList] = await Promise.all([
				fetchPets(uid),
				fetchSchedules(uid),
			])

			if (requestId !== schedulesRequestId) return
			schedules.value = enrichSchedules(scheduleList, petList)
		} catch (e) {
			if (requestId !== schedulesRequestId) return
			console.warn('加载日程失败', e)
			schedules.value = []
		} finally {
			if (requestId === schedulesRequestId) {
				loading.value = false
				hasLoaded.value = true
			}
		}
	}

	async function fetchPets(uid : string) {
		const res = await uniCloud.callFunction({ name: 'petManager', data: { action: 'list', uid } })
		const list = res.result?.code === 0 ? (res.result.data || []) as Pet[] : []
		await resolveAvatars(list)
		return list
	}

	async function fetchSchedules(uid : string) {
		const res = await uniCloud.callFunction({ name: 'scheduleManager', data: { action: 'list', uid } })
		if (res.result?.code !== 0) throw new Error(res.result?.message || '读取日程失败')
		return (res.result.data?.list || []) as ScheduleItem[]
	}

	async function resolveAvatars(list : Pet[]) {
		const cloudList = list.filter((pet) => pet.avatarUrl?.startsWith('cloud://'))
		list.forEach((pet) => {
			if (pet.avatarUrl && !pet.avatarUrl.startsWith('cloud://')) pet._displayAvatar = pet.avatarUrl
		})
		if (!cloudList.length) return
		try {
			const res = await uniCloud.getTempFileURL({ fileList: cloudList.map((pet) => pet.avatarUrl) })
			const urlMap : Record<string, string> = {}
			res.fileList?.forEach((item : any) => {
				if (item.tempFileURL) urlMap[item.fileID] = item.tempFileURL
			})
			cloudList.forEach((pet) => {
				pet._displayAvatar = urlMap[pet.avatarUrl || ''] || ''
			})
		} catch (e) {
			console.warn('解析宠物头像失败', e)
		}
	}

	function enrichSchedules(list : ScheduleItem[], petList : Pet[]) {
		const petMap = new Map(petList.map((pet) => [pet._id, pet]))
		return [...list]
			.map((item) => {
				const pet = petMap.get(item.pet_id)
				return {
					...item,
					frequency: item.frequency || 'once',
					status: item.status || 'pending',
					petName: pet?.name || item.petName || '',
					petAvatar: pet?._displayAvatar || item.petAvatar || '',
					derivedStatus: getDerivedStatus(item),
				}
			})
			.sort((a, b) => {
				const statusWeight : Record<DerivedStatus, number> = { overdue: 0, today: 1, pending: 2, skipped: 3, completed: 4 }
				const diff = statusWeight[a.derivedStatus] - statusWeight[b.derivedStatus]
				if (diff !== 0) return diff
				return getTimeValue(a.scheduledDate) - getTimeValue(b.scheduledDate)
			})
	}

	function getDerivedStatus(item : ScheduleItem) : DerivedStatus {
		if (item.status === 'completed') return 'completed'
		if (item.status === 'skipped') return 'skipped'
		const date = getDate(item.scheduledDate)
		if (!date) return item.status || 'pending'
		const dayDiff = diffDays(startOfDay(date), startOfDay(new Date()))
		if (dayDiff < 0) return 'overdue'
		if (dayDiff === 0) return 'today'
		return 'pending'
	}

	function getTypeStyle(type : string) {
		return typeStyleMap[type] || typeStyleMap.custom
	}

	function getStatusStyle(status : DerivedStatus) {
		return statusStyleMap[status] || statusStyleMap.pending
	}

	function getTypeLabel(type : string) {
		return typeLabelMap[type] || '提醒'
	}

	function getFrequencyLabel(value ?: string) {
		return frequencyLabelMap[value || 'once'] || '单次'
	}

	function getDueText(item : ScheduleItem) {
		if (item.derivedStatus === 'completed') return '已完成'
		if (item.derivedStatus === 'skipped') return '已跳过'
		const date = getDate(item.scheduledDate)
		if (!date) return '待安排'
		const days = diffDays(startOfDay(date), startOfDay(new Date()))
		if (days < 0) return `逾期${Math.abs(days)}天`
		if (days === 0) return '今天'
		if (days === 1) return '明天'
		return `${days}天后`
	}

	function formatMonth(value : number | string) {
		const date = getDate(value)
		return date ? `${date.getMonth() + 1}月` : '--'
	}

	function formatDay(value : number | string) {
		const date = getDate(value)
		return date ? String(date.getDate()).padStart(2, '0') : '--'
	}

	function formatDate(value : number | string) {
		const date = getDate(value)
		if (!date) return '日期未定'
		const days = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
		return `${date.getMonth() + 1}月${date.getDate()}日 ${days[date.getDay()]}`
	}

	function getDate(value : number | string | undefined) {
		if (!value) return null
		if (typeof value === 'string') {
			const dateOnly = value.match(/^(\d{4})[-/](\d{1,2})[-/](\d{1,2})/)
			if (dateOnly) {
				return new Date(Number(dateOnly[1]), Number(dateOnly[2]) - 1, Number(dateOnly[3]))
			}
		}
		const date = new Date(value)
		return Number.isNaN(date.getTime()) ? null : date
	}

	function getTimeValue(value : number | string | undefined) {
		return getDate(value)?.getTime() || 0
	}

	function startOfDay(date : Date) {
		return new Date(date.getFullYear(), date.getMonth(), date.getDate())
	}

	function addDays(date : Date, days : number) {
		const next = new Date(date)
		next.setDate(next.getDate() + days)
		return next
	}

	function diffDays(left : Date, right : Date) {
		return Math.round((left.getTime() - right.getTime()) / 86400000)
	}

	function isSameDay(left : Date | null, right : Date | null) {
		if (!left || !right) return false
		return left.getFullYear() === right.getFullYear()
			&& left.getMonth() === right.getMonth()
			&& left.getDate() === right.getDate()
	}

	function formatDateKey(date : Date) {
		return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`
	}

	function handleAddSchedule() {
		uni.navigateTo({ url: '/pages/schedule/edit' })
	}

	function editSchedule(item : ScheduleItem) {
		uni.navigateTo({ url: `/pages/schedule/edit?id=${item._id}` })
	}

	async function toggleComplete(item : ScheduleItem) {
		try {
			const uid = uni.getStorageSync('uid')
			const nextStatus = item.derivedStatus === 'completed' ? 'pending' : 'completed'
			const res = await uniCloud.callFunction({
				name: 'scheduleManager',
				data: {
					action: 'update',
					uid,
					scheduleId: item._id,
					status: nextStatus,
				},
			})
			if (res.result?.code !== 0) throw new Error(res.result?.message || '更新失败')
			uni.showToast({ title: nextStatus === 'completed' ? '已完成' : '已恢复', icon: 'success' })
			loadSchedules(false)
		} catch (e) {
			console.error('更新提醒状态失败', e)
			uni.showToast({ title: '操作失败，请稍后再试', icon: 'none' })
		}
	}

	function deleteSchedule(item : ScheduleItem) {
		uni.showModal({
			title: '删除提醒',
			content: `确认删除「${item.title || getTypeLabel(item.type)}」吗？`,
			confirmText: '删除',
			confirmColor: '#DC2626',
			success: async (res) => {
				if (!res.confirm) return
				try {
					const uid = uni.getStorageSync('uid')
					const result = await uniCloud.callFunction({
						name: 'scheduleManager',
						data: { action: 'delete', uid, scheduleId: item._id },
					})
					if (result.result?.code !== 0) throw new Error(result.result?.message || '删除失败')
					uni.showToast({ title: '已删除', icon: 'success' })
					loadSchedules(false)
				} catch (e) {
					console.error('删除提醒失败', e)
					uni.showToast({ title: '删除失败，请稍后再试', icon: 'none' })
				}
			},
		})
	}
</script>

<style lang="scss" scoped>
	$primary: #F97316;
	$primary-light: #FB923C;
	$primary-bg: #FFF7ED;
	$teal: #0D9488;
	$text-dark: #0F172A;
	$text-body: #475569;
	$text-muted: #64748B;
	$border: #E2E8F0;
	$card-bg: #FFFFFF;
	$shadow: 0 8rpx 24rpx rgba(15, 23, 42, 0.06);
	$shadow-active: 0 12rpx 34rpx rgba(15, 23, 42, 0.1);

	.schedules-page {
		position: relative;
		height: 100%;
		background: #F8FAFC;
	}

	.schedules-scroll {
		height: 100vh;
		box-sizing: border-box;
	}

	.hero-panel {
		margin: 24rpx 24rpx 0;
		padding: 30rpx 28rpx 26rpx;
		border-radius: 28rpx;
		background: linear-gradient(135deg, #F97316 0%, #FB923C 56%, #14B8A6 150%);
		box-shadow: 0 14rpx 36rpx rgba(249, 115, 22, 0.22);
		overflow: hidden;
	}

	.hero-main {
		display: flex;
		align-items: flex-start;
		justify-content: space-between;
		gap: 24rpx;
	}

	.hero-copy {
		flex: 1;
		min-width: 0;
		display: flex;
		flex-direction: column;
	}

	.hero-kicker {
		font-size: 23rpx;
		font-weight: 800;
		color: rgba(255, 255, 255, 0.78);
	}

	.hero-title {
		margin-top: 8rpx;
		font-size: 39rpx;
		font-weight: 900;
		color: #FFFFFF;
		line-height: 1.25;
	}

	.hero-desc {
		margin-top: 12rpx;
		font-size: 25rpx;
		color: rgba(255, 255, 255, 0.84);
		line-height: 1.5;
	}

	.hero-action {
		width: 76rpx;
		height: 76rpx;
		border-radius: 24rpx;
		background: rgba(255, 255, 255, 0.2);
		border: 1rpx solid rgba(255, 255, 255, 0.28);
		display: flex;
		align-items: center;
		justify-content: center;
		flex-shrink: 0;
		transition: opacity 0.2s ease;

		&:active {
			opacity: 0.78;
		}
	}

	.summary-row {
		margin-top: 28rpx;
		display: flex;
		align-items: center;
		padding: 18rpx 0;
		border-radius: 18rpx;
		background: rgba(255, 255, 255, 0.16);
	}

	.summary-item {
		flex: 1;
		min-width: 0;
		display: flex;
		flex-direction: column;
		align-items: center;
		gap: 4rpx;
	}

	.summary-value {
		font-size: 32rpx;
		font-weight: 900;
		color: #FFFFFF;
	}

	.summary-label {
		font-size: 21rpx;
		color: rgba(255, 255, 255, 0.78);
	}

	.summary-divider {
		width: 1rpx;
		height: 42rpx;
		background: rgba(255, 255, 255, 0.28);
	}

	.week-strip {
		margin: 20rpx 24rpx 0;
		padding: 16rpx 12rpx;
		border-radius: 24rpx;
		background: $card-bg;
		box-shadow: $shadow;
		display: grid;
		grid-template-columns: repeat(7, 1fr);
		gap: 6rpx;
	}

	.week-day {
		min-height: 92rpx;
		border-radius: 18rpx;
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		gap: 5rpx;
		color: $text-muted;
	}

	.week-day-active {
		background: $primary-bg;
		color: $primary;
	}

	.week-name {
		font-size: 20rpx;
		font-weight: 700;
	}

	.week-date {
		font-size: 28rpx;
		font-weight: 900;
	}

	.week-dot {
		width: 8rpx;
		height: 8rpx;
		border-radius: 50%;
		background: transparent;
	}

	.week-dot-on {
		background: $primary;
	}

	.tab-card {
		display: grid;
		place-items: center;
		height: 82rpx;
		position: sticky;
		top: 0;
		z-index: 20;
		margin: 20rpx 24rpx 0;
		padding: 4rpx;
		border-radius: 22rpx;
		background: $card-bg;
		box-shadow: $shadow;
		overflow: hidden;
	}

	.tab-card :deep(.tn-tabs) {
		width: 100%;
		align-self: center;
	}

	.tab-card :deep(.tn-tabs__container) {
		align-items: center;
	}

	.tab-card :deep(.tn-tabs__bar-container) {
		bottom: 4rpx;
	}

	.schedules-stage {
		min-height: 720rpx;
	}

	.loading-state {
		padding: 48rpx 24rpx 0;
		display: flex;
		flex-direction: column;
		align-items: center;
		gap: 18rpx;
	}

	.loading-text {
		font-size: 26rpx;
		color: $text-muted;
		margin-bottom: 10rpx;
	}

	.skeleton-card {
		width: 100%;
		box-sizing: border-box;
		display: flex;
		align-items: center;
		gap: 20rpx;
		padding: 24rpx;
		border-radius: 24rpx;
		background: $card-bg;
		box-shadow: $shadow;
	}

	.skeleton-date {
		width: 88rpx;
		height: 96rpx;
		border-radius: 22rpx;
		background: #E2E8F0;
		flex-shrink: 0;
	}

	.skeleton-content {
		flex: 1;
		display: flex;
		flex-direction: column;
		gap: 14rpx;
	}

	.skeleton-line {
		height: 24rpx;
		border-radius: 12rpx;
		background: #E2E8F0;
	}

	.skeleton-line-title {
		width: 64%;
	}

	.skeleton-line-meta {
		width: 42%;
	}

	.empty-state {
		min-height: 620rpx;
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		gap: 18rpx;
		padding: 0 48rpx;
	}

	.empty-title {
		font-size: 32rpx;
		font-weight: 800;
		color: $text-dark;
	}

	.empty-desc {
		max-width: 520rpx;
		font-size: 26rpx;
		line-height: 1.6;
		text-align: center;
		color: $text-muted;
		margin-bottom: 10rpx;
	}

	.schedule-content {
		padding: 26rpx 24rpx 0;
	}

	.section-head {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: 20rpx;
		margin-bottom: 18rpx;
	}

	.section-title-block {
		flex: 1;
		min-width: 0;
		display: flex;
		flex-direction: column;
		gap: 6rpx;
	}

	.section-title {
		font-size: 31rpx;
		font-weight: 900;
		color: $text-dark;
	}

	.section-subtitle {
		font-size: 23rpx;
		color: $text-muted;
	}

	.schedule-list {
		display: flex;
		flex-direction: column;
		gap: 16rpx;
	}

	.schedule-card {
		display: flex;
		align-items: stretch;
		gap: 18rpx;
		min-height: 178rpx;
		padding: 22rpx 18rpx 22rpx 20rpx;
		border-radius: 24rpx;
		background: $card-bg;
		box-shadow: $shadow;
		transition: transform 0.18s ease, box-shadow 0.18s ease, opacity 0.18s ease;

		&.schedule-card-active {
			transform: scale(0.985);
			box-shadow: $shadow-active;
		}
	}

	.schedule-card-completed {
		opacity: 0.76;
	}

	.date-chip {
		width: 88rpx;
		border-radius: 22rpx;
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		gap: 2rpx;
		flex-shrink: 0;
	}

	.date-chip-pending {
		background: #EFF6FF;
		color: #2563EB;
	}

	.date-chip-today {
		background: $primary-bg;
		color: $primary;
	}

	.date-chip-overdue {
		background: #FEF2F2;
		color: #DC2626;
	}

	.date-chip-completed {
		background: #F0FDFA;
		color: $teal;
	}

	.date-chip-skipped {
		background: #F8FAFC;
		color: $text-muted;
	}

	.date-month {
		font-size: 20rpx;
		font-weight: 700;
	}

	.date-day {
		font-size: 36rpx;
		font-weight: 900;
	}

	.schedule-main {
		flex: 1;
		min-width: 0;
		display: flex;
		flex-direction: column;
		gap: 10rpx;
	}

	.title-row,
	.pet-row,
	.meta-row {
		display: flex;
		align-items: center;
		min-width: 0;
	}

	.title-row {
		gap: 10rpx;
	}

	.type-icon {
		width: 48rpx;
		height: 48rpx;
		border-radius: 15rpx;
		display: flex;
		align-items: center;
		justify-content: center;
		flex-shrink: 0;
	}

	.schedule-title {
		flex: 1;
		min-width: 0;
		font-size: 30rpx;
		font-weight: 850;
		color: $text-dark;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}

	.pet-row {
		gap: 9rpx;
	}

	.pet-name {
		flex: 1;
		min-width: 0;
		font-size: 24rpx;
		color: $text-body;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}

	.meta-row {
		flex-wrap: wrap;
		gap: 10rpx;
	}

	.meta-pill {
		max-width: 220rpx;
		height: 40rpx;
		box-sizing: border-box;
		display: flex;
		align-items: center;
		gap: 5rpx;
		padding: 0 11rpx;
		border-radius: 999rpx;
		background: #F8FAFC;
		border: 1rpx solid $border;
		font-size: 21rpx;
		color: $text-body;

		text {
			overflow: hidden;
			text-overflow: ellipsis;
			white-space: nowrap;
		}
	}

	.schedule-note {
		font-size: 23rpx;
		color: $text-muted;
		line-height: 1.45;
		overflow: hidden;
		text-overflow: ellipsis;
		display: -webkit-box;
		-webkit-line-clamp: 2;
		-webkit-box-orient: vertical;
	}

	.action-col {
		width: 64rpx;
		display: flex;
		flex-direction: column;
		justify-content: center;
		gap: 12rpx;
		flex-shrink: 0;
	}

	.icon-button {
		width: 62rpx;
		height: 62rpx;
		border-radius: 20rpx;
		background: #F8FAFC;
		border: 1rpx solid $border;
		display: flex;
		align-items: center;
		justify-content: center;
		transition: opacity 0.18s ease;

		&:active {
			opacity: 0.68;
		}
	}

	.danger-button {
		background: #FEF2F2;
		border-color: #FEE2E2;
	}

	.bottom-tip {
		height: 86rpx;
		display: flex;
		align-items: center;
		justify-content: center;
		gap: 8rpx;
		font-size: 23rpx;
		color: $text-muted;
	}

	.button-inner {
		display: flex;
		align-items: center;
		justify-content: center;
		gap: 10rpx;
	}

	.fab-wrap {
		position: fixed;
		left: 50%;
		transform: translateX(-50%);
		z-index: 60;
	}
</style>
