<template>
	<view class="discovery-page">
		<scroll-view
			class="discovery-scroll"
			scroll-y
			:scroll-with-animation="true"
			:style="{ paddingBottom: scrollPaddingBottom + 'px' }"
		>
			<view class="hero-panel">
				<view class="hero-copy">
					<text class="hero-kicker">发现</text>
					<text class="hero-title">附近服务与养宠百科</text>
					<text class="hero-desc">
						需要就医、查知识、找协寻入口时，先来这里快速判断下一步。
					</text>
				</view>

				<view class="hero-stats">
					<view class="hero-stat">
						<text class="hero-stat-value">{{ hospitalCountText }}</text>
						<text class="hero-stat-label">医院</text>
					</view>
					<view class="hero-stat-divider" />
					<view class="hero-stat">
						<text class="hero-stat-value">{{ articleCountText }}</text>
						<text class="hero-stat-label">百科</text>
					</view>
					<view class="hero-stat-divider" />
					<view class="hero-stat">
						<text class="hero-stat-value">{{ nearestDistanceText }}</text>
						<text class="hero-stat-label">最近</text>
					</view>
				</view>
			</view>

			<view class="search-card">
				<TnSearchBox
					v-model="keyword"
					placeholder="搜索医院、症状、疫苗、驱虫"
					shape="round"
					size="lg"
					border-color="#FED7AA"
					search-button-bg-color="#F97316"
					search-button-text-color="#FFFFFF"
					search-button-text="搜索"
					@search="handleSearch"
					@clear="handleClearSearch"
				/>
			</view>

			<view class="tab-card">
				<TnTabs
					v-model="currentTabIndex"
					height="76rpx"
					bar-width="38rpx"
					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 mainTabs" :key="tab" :title="tab" />
				</TnTabs>
			</view>

			<view v-if="currentTabIndex === 0" class="panel-section">
				<view class="section-head">
					<view class="section-title-block">
						<text class="section-title">附近宠物医院</text>
						<text class="section-subtitle">{{ locationHint }}</text>
					</view>
					<TnButton
						:custom-style="outlineButtonStyle"
						text-color="#F97316"
						shape="round"
						width="172"
						height="66"
						:font-size="24"
						@click="requestLocation"
					>
						<view class="button-inner">
							<TnIcon name="location" :size="24" />
							<text>{{ locationButtonText }}</text>
						</view>
					</TnButton>
				</view>

				<view class="distance-filter">
					<view
						v-for="range in distanceRanges"
						:key="range.value"
						class="distance-chip"
						:class="{ 'distance-chip-active': selectedDistance === range.value }"
						@click="selectDistance(range.value)"
					>
						<text>{{ range.label }}</text>
					</view>
				</view>

				<view v-if="loadingHospitals" class="loading-state">
					<TnLoading :show="true" mode="circle" color="#F97316" :size="52" />
					<text class="loading-text">正在整理附近医院...</text>
				</view>

				<view v-else-if="filteredHospitals.length === 0" class="empty-state">
					<TnEmpty mode="data" color="#F97316" size="lg" />
					<text class="empty-title">暂时没有匹配的医院</text>
					<text class="empty-desc">换个关键词，或切换到更大的距离范围看看。</text>
				</view>

				<view v-else class="hospital-list">
					<view
						v-for="hospital in filteredHospitals"
						:key="hospital._id"
						class="hospital-card"
						hover-class="card-active"
						@click="previewHospital(hospital)"
					>
						<view class="hospital-accent" :class="{ 'accent-open': hospital.is24Hour }" />
						<view class="hospital-main">
							<view class="hospital-title-row">
								<text class="hospital-name">{{ hospital.name }}</text>
								<TnTag
									v-if="hospital.verified"
									bg-color="#ECFDF5"
									text-color="#059669"
									shape="round"
									size="sm"
								>
									已认证
								</TnTag>
							</view>
							<text class="hospital-address">{{ hospital.address }}</text>
							<view class="hospital-meta">
								<view class="meta-pill">
									<TnIcon name="star-fill" :size="21" color="#F59E0B" />
									<text>{{ formatRating(hospital.rating) }}</text>
								</view>
								<view class="meta-pill">
									<TnIcon name="location" :size="21" color="#64748B" />
									<text>{{ hospital.distanceText || '距离未知' }}</text>
								</view>
								<view v-if="hospital.is24Hour" class="meta-pill urgent-pill">
									<text>24小时</text>
								</view>
							</view>
							<view class="tag-row">
								<TnTag
									v-for="tag in hospital.tags.slice(0, 3)"
									:key="tag"
									bg-color="#FFF7ED"
									text-color="#F97316"
									shape="round"
									size="sm"
								>
									{{ tag }}
								</TnTag>
							</view>
						</view>
						<view class="hospital-actions" @click.stop>
							<view
								class="icon-button"
								:class="{ 'icon-button-disabled': !hospital.phone }"
								@click="callHospital(hospital)"
							>
								<TnIcon name="tel" :size="28" :color="hospital.phone ? '#0D9488' : '#CBD5E1'" />
							</view>
							<view class="icon-button" @click="openHospitalLocation(hospital)">
								<TnIcon name="map" :size="28" color="#F97316" />
							</view>
						</view>
					</view>
				</view>
			</view>

			<view v-else-if="currentTabIndex === 1" class="panel-section">
				<view class="section-head">
					<view class="section-title-block">
						<text class="section-title">养宠百科</text>
						<text class="section-subtitle">按场景查，先处理紧急程度，再补记录。</text>
					</view>
					<TnTag bg-color="#F0FDFA" text-color="#0D9488" shape="round" size="sm">
						{{ filteredArticles.length }} 篇
					</TnTag>
				</view>

				<scroll-view class="category-scroll" scroll-x :show-scrollbar="false">
					<view class="category-track">
						<view
							v-for="category in articleCategories"
							:key="category.value"
							class="category-chip"
							:class="{ 'category-chip-active': selectedArticleCategory === category.value }"
							@click="selectArticleCategory(category.value)"
						>
							<TnIcon :name="category.icon" :size="24" />
							<text>{{ category.label }}</text>
						</view>
					</view>
				</scroll-view>

				<view v-if="loadingArticles" class="loading-state">
					<TnLoading :show="true" mode="circle" color="#0D9488" :size="52" />
					<text class="loading-text">正在读取百科内容...</text>
				</view>

				<view v-else-if="filteredArticles.length === 0" class="empty-state">
					<TnEmpty mode="search" color="#0D9488" size="lg" />
					<text class="empty-title">没搜到相关百科</text>
					<text class="empty-desc">试试「呕吐」「疫苗」「驱虫」这类更短的关键词。</text>
				</view>

				<view v-else class="article-list">
					<view
						v-for="article in filteredArticles"
						:key="article._id"
						class="article-card"
						hover-class="card-active"
						@click="openArticle(article)"
					>
						<view class="article-cover" :style="{ background: getArticleCover(article.category) }">
							<TnIcon :name="getArticleIcon(article.category)" :size="48" color="#FFFFFF" />
						</view>
						<view class="article-main">
							<view class="article-title-row">
								<text class="article-title">{{ article.title }}</text>
								<TnTag :bg-color="getCategoryStyle(article.category).bg" :text-color="getCategoryStyle(article.category).color" shape="round" size="sm">
									{{ getCategoryLabel(article.category) }}
								</TnTag>
							</view>
							<text class="article-summary">{{ article.summary }}</text>
							<view class="article-meta">
								<view class="meta-pill">
									<TnIcon name="eye" :size="20" color="#64748B" />
									<text>{{ article.viewCount || 0 }}</text>
								</view>
								<view class="meta-pill">
									<TnIcon name="floral" :size="20" color="#64748B" />
									<text>{{ getPetTypeLabel(article.petType) }}</text>
								</view>
								<view class="article-tags">
									<text v-for="tag in article.tags.slice(0, 2)" :key="tag">#{{ tag }}</text>
								</view>
							</view>
						</view>
						<TnIcon name="right" :size="28" color="#CBD5E1" />
					</view>
				</view>
			</view>

			<view v-else class="panel-section">
				<view class="assist-card">
					<view class="assist-icon">
						<TnIcon name="notice-fill" :size="54" color="#FFFFFF" />
					</view>
					<view class="assist-copy">
						<text class="assist-title">宠物丢失协寻</text>
						<text class="assist-desc">
							产品文档把协寻列为社区可选能力。当前先保留发布入口与流程说明，等内容安全审核和附近推送接入后即可开放。
						</text>
					</view>
				</view>

				<view class="assist-steps">
					<view v-for="step in lostPetSteps" :key="step.title" class="step-row">
						<view class="step-icon">
							<TnIcon :name="step.icon" :size="30" color="#F97316" />
						</view>
						<view class="step-copy">
							<text class="step-title">{{ step.title }}</text>
							<text class="step-desc">{{ step.desc }}</text>
						</view>
					</view>
				</view>

				<TnButton
					text-color="#FFFFFF"
					shape="round"
					:bold="true"
					:custom-style="primaryButtonStyle"
					width="100%"
					height="88"
					:font-size="28"
					@click="publishLostPet"
				>
					<view class="button-inner">
						<TnIcon name="add-lack" :size="28" />
						<text>发布丢失启事</text>
					</view>
				</TnButton>
			</view>
		</scroll-view>
	</view>
</template>

<script setup lang="ts">
	import { computed, onMounted, ref } from '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 TnSearchBox from '@/uni_modules/tuniaoui-vue3/components/search-box/src/search-box.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'

	interface Hospital {
		_id : string
		name : string
		address : string
		location ?: {
			type : string
			coordinates : number[]
		}
		phone ?: string
		hours ?: string
		is24Hour : boolean
		tags : string[]
		rating ?: number
		reviewCount ?: number
		verified ?: boolean
		status ?: string
		distance ?: number | null
		distanceText ?: string
	}

	interface Article {
		_id : string
		title : string
		category : string
		petType : string
		cover ?: string
		content ?: string
		summary : string
		tags : string[]
		viewCount ?: number
		status ?: string
		createdAt ?: number | string
	}

	interface CategoryStyle {
		label : string
		icon : string
		color : string
		bg : string
		cover : string
	}

	const mainTabs = ['附近医院', '养宠百科', '丢失协寻']
	const currentTabIndex = ref(0)
	const keyword = ref('')
	const activeKeyword = ref('')
	const loadingHospitals = ref(true)
	const loadingArticles = ref(true)
	const selectedDistance = ref(5000)
	const selectedArticleCategory = ref('all')
	const userLocation = ref<{ longitude : number; latitude : number } | null>(null)
	const hospitals = ref<Hospital[]>([])
	const articles = ref<Article[]>([])
	const scrollPaddingBottom = ref(78)

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

	const outlineButtonStyle = {
		background: '#FFF7ED',
		border: '1rpx solid #FED7AA',
	}

	const distanceRanges = [
		{ label: '3km', value: 3000 },
		{ label: '5km', value: 5000 },
		{ label: '10km', value: 10000 },
	]

	const categoryStyles : Record<string, CategoryStyle> = {
		vaccine: {
			label: '疫苗',
			icon: 'safe-fill',
			color: '#2563EB',
			bg: '#EFF6FF',
			cover: 'linear-gradient(135deg, #2563EB, #60A5FA)',
		},
		deworm: {
			label: '驱虫',
			icon: 'notice-fill',
			color: '#7C3AED',
			bg: '#F5F3FF',
			cover: 'linear-gradient(135deg, #7C3AED, #A78BFA)',
		},
		disease: {
			label: '疾病',
			icon: 'medical',
			color: '#DC2626',
			bg: '#FEF2F2',
			cover: 'linear-gradient(135deg, #DC2626, #FB7185)',
		},
		nutrition: {
			label: '营养',
			icon: 'shop-fill',
			color: '#0D9488',
			bg: '#F0FDFA',
			cover: 'linear-gradient(135deg, #0D9488, #2DD4BF)',
		},
		behavior: {
			label: '行为',
			icon: 'floral',
			color: '#F97316',
			bg: '#FFF7ED',
			cover: 'linear-gradient(135deg, #F97316, #FB923C)',
		},
		other: {
			label: '其他',
			icon: 'inventory-fill',
			color: '#475569',
			bg: '#F8FAFC',
			cover: 'linear-gradient(135deg, #64748B, #94A3B8)',
		},
	}

	const articleCategories = [
		{ label: '全部', value: 'all', icon: 'menu-grille' },
		...Object.entries(categoryStyles).map(([value, item]) => ({
			label: item.label,
			value,
			icon: item.icon,
		})),
	]

	const lostPetSteps = [
		{ title: '填写信息', desc: '照片、特征、丢失地点、时间和联系方式。', icon: 'edit-write' },
		{ title: '内容审核', desc: '接入平台内容安全后，先审核再展示。', icon: 'safe' },
		{ title: '附近推送', desc: '按地理位置提醒附近用户，发现者可反馈位置。', icon: 'location' },
	]

	const fallbackHospitals : Hospital[] = [
		{
			_id: 'seed-hospital-1',
			name: '暖爪宠物医院',
			address: '示例城市 梧桐路 18 号',
			location: { type: 'Point', coordinates: [121.4737, 31.2304] },
			phone: '400-000-1024',
			hours: '09:00-21:00',
			is24Hour: false,
			tags: ['疫苗', '猫科', '体检'],
			rating: 4.8,
			reviewCount: 236,
			verified: true,
			status: 'active',
		},
		{
			_id: 'seed-hospital-2',
			name: '夜航动物急诊中心',
			address: '示例城市 星河大道 66 号',
			location: { type: 'Point', coordinates: [121.489, 31.222] },
			phone: '400-000-2048',
			hours: '24小时',
			is24Hour: true,
			tags: ['急诊', '影像', '住院'],
			rating: 4.6,
			reviewCount: 189,
			verified: true,
			status: 'active',
		},
		{
			_id: 'seed-hospital-3',
			name: '小橘子宠物诊所',
			address: '示例城市 花园街 9 号',
			location: { type: 'Point', coordinates: [121.451, 31.238] },
			phone: '400-000-4096',
			hours: '10:00-20:00',
			is24Hour: false,
			tags: ['皮肤科', '驱虫', '绝育'],
			rating: 4.5,
			reviewCount: 94,
			verified: false,
			status: 'active',
		},
	]

	const fallbackArticles : Article[] = [
		{
			_id: 'seed-article-1',
			title: '幼猫第一年疫苗怎么安排',
			category: 'vaccine',
			petType: 'cat',
			summary: '梳理妙三多、狂犬和加强针的常见节奏，帮助你和医生确认计划。',
			tags: ['幼猫', '妙三多', '狂犬'],
			viewCount: 1280,
			status: 'published',
		},
		{
			_id: 'seed-article-2',
			title: '猫咪呕吐时先观察什么',
			category: 'disease',
			petType: 'cat',
			summary: '从频率、精神、食欲、异物风险判断是否需要尽快就医。',
			tags: ['呕吐', '急诊', '观察'],
			viewCount: 2136,
			status: 'published',
		},
		{
			_id: 'seed-article-3',
			title: '体内外驱虫的间隔怎么记',
			category: 'deworm',
			petType: 'all',
			summary: '按品牌和生活环境估算默认周期，再同步到提醒日程。',
			tags: ['驱虫', '提醒', '周期'],
			viewCount: 986,
			status: 'published',
		},
		{
			_id: 'seed-article-4',
			title: '换粮期便便变软怎么办',
			category: 'nutrition',
			petType: 'dog',
			summary: '用 7 天过渡法降低肠胃压力，并记录体重和便便状态。',
			tags: ['换粮', '软便', '营养'],
			viewCount: 742,
			status: 'published',
		},
	]

	const hospitalCountText = computed(() => String(hospitals.value.length))
	const articleCountText = computed(() => String(articles.value.length))

	const nearestDistanceText = computed(() => {
		const nearest = filteredHospitals.value.find((item) => item.distanceText)
		return nearest?.distanceText || '待定位'
	})

	const locationHint = computed(() => {
		if (userLocation.value) return `已按 ${selectedDistance.value / 1000}km 范围优先排序`
		return '授权定位后，可按距离排序并一键导航'
	})

	const locationButtonText = computed(() => (userLocation.value ? '重定位' : '定位'))

	const filteredHospitals = computed(() => {
		const text = normalizeKeyword(activeKeyword.value)
		return hospitals.value
			.filter((hospital) => {
				const distanceOk = !userLocation.value || !hospital.distance || hospital.distance <= selectedDistance.value
				if (!distanceOk) return false
				if (!text) return true
				return normalizeKeyword([
					hospital.name,
					hospital.address,
					hospital.hours,
					...(hospital.tags || []),
				].join(' ')).includes(text)
			})
			.sort((a, b) => {
				if (a.distance != null && b.distance != null) return a.distance - b.distance
				if (a.is24Hour !== b.is24Hour) return a.is24Hour ? -1 : 1
				return (b.rating || 0) - (a.rating || 0)
			})
	})

	const filteredArticles = computed(() => {
		const text = normalizeKeyword(activeKeyword.value)
		return articles.value.filter((article) => {
			const categoryOk = selectedArticleCategory.value === 'all' || article.category === selectedArticleCategory.value
			if (!categoryOk) return false
			if (!text) return true
			return normalizeKeyword([
				article.title,
				article.summary,
				getCategoryLabel(article.category),
				getPetTypeLabel(article.petType),
				...(article.tags || []),
			].join(' ')).includes(text)
		})
	})

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

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

	async function loadDiscoveryData() {
		await Promise.all([loadHospitals(), loadArticles()])
	}

	async function loadHospitals() {
		loadingHospitals.value = true
		try {
			const db = uniCloud.database()
			const res = await db.collection('hospitals')
				.where('status == "active"')
				.limit(50)
				.get()
			const list = normalizeHospitals((res.result?.data || res.data || []) as Hospital[])
			hospitals.value = list.length ? enrichHospitalDistance(list) : enrichHospitalDistance(fallbackHospitals)
		} catch (e) {
			console.warn('加载医院数据失败，使用本地种子数据', e)
			hospitals.value = enrichHospitalDistance(fallbackHospitals)
		} finally {
			loadingHospitals.value = false
		}
	}

	async function loadArticles() {
		loadingArticles.value = true
		try {
			const db = uniCloud.database()
			const res = await db.collection('articles')
				.where('status == "published"')
				.orderBy('createdAt', 'desc')
				.limit(30)
				.get()
			const list = normalizeArticles((res.result?.data || res.data || []) as Article[])
			articles.value = list.length ? list : fallbackArticles
		} catch (e) {
			console.warn('加载百科数据失败，使用本地种子数据', e)
			articles.value = fallbackArticles
		} finally {
			loadingArticles.value = false
		}
	}

	function normalizeHospitals(list : Hospital[]) {
		return list.map((item) => ({
			...item,
			is24Hour: Boolean(item.is24Hour),
			tags: Array.isArray(item.tags) ? item.tags : [],
			distance: item.distance ?? null,
			distanceText: item.distanceText || formatDistance(item.distance),
		}))
	}

	function normalizeArticles(list : Article[]) {
		return list.map((item) => ({
			...item,
			petType: item.petType || 'all',
			summary: item.summary || '暂无摘要，点击查看详情。',
			tags: Array.isArray(item.tags) ? item.tags : [],
			viewCount: item.viewCount || 0,
		}))
	}

	function enrichHospitalDistance(list : Hospital[]) {
		if (!userLocation.value) {
			return list.map((item) => ({ ...item, distanceText: item.distanceText || formatDistance(item.distance) }))
		}
		return list.map((item) => {
			const coordinates = item.location?.coordinates
			const distance = coordinates?.length === 2
				? calcDistance(userLocation.value!.latitude, userLocation.value!.longitude, coordinates[1], coordinates[0])
				: item.distance
			return {
				...item,
				distance,
				distanceText: formatDistance(distance),
			}
		})
	}

	function requestLocation() {
		loadingHospitals.value = true
		uni.getLocation({
			type: 'gcj02',
			success: (res) => {
				userLocation.value = {
					longitude: res.longitude,
					latitude: res.latitude,
				}
				hospitals.value = enrichHospitalDistance(hospitals.value.length ? hospitals.value : fallbackHospitals)
				uni.showToast({ title: '已按距离更新', icon: 'success' })
			},
			fail: () => {
				uni.showModal({
					title: '需要定位权限',
					content: '开启定位后才能按距离筛选附近宠物医院，也可以先查看默认推荐。',
					confirmText: '知道了',
					showCancel: false,
				})
			},
			complete: () => {
				loadingHospitals.value = false
			},
		})
	}

	function selectDistance(value : number) {
		selectedDistance.value = value
	}

	function selectArticleCategory(value : string) {
		selectedArticleCategory.value = value
	}

	function handleSearch(value : string) {
		activeKeyword.value = value.trim()
	}

	function handleClearSearch() {
		keyword.value = ''
		activeKeyword.value = ''
	}

	function previewHospital(hospital : Hospital) {
		uni.showModal({
			title: hospital.name,
			content: `${hospital.address}\n${hospital.hours || '营业时间待补充'}\n${hospital.tags.join(' / ') || '暂无特色标签'}`,
			confirmText: '导航',
			cancelText: '关闭',
			success: (res) => {
				if (res.confirm) openHospitalLocation(hospital)
			},
		})
	}

	function callHospital(hospital : Hospital) {
		if (!hospital.phone) {
			uni.showToast({ title: '暂无电话', icon: 'none' })
			return
		}
		uni.makePhoneCall({
			phoneNumber: hospital.phone,
			fail: () => uni.showToast({ title: '无法拨打电话', icon: 'none' }),
		})
	}

	function openHospitalLocation(hospital : Hospital) {
		const coordinates = hospital.location?.coordinates
		if (!coordinates || coordinates.length !== 2) {
			uni.showToast({ title: '暂无导航位置', icon: 'none' })
			return
		}
		uni.openLocation({
			longitude: coordinates[0],
			latitude: coordinates[1],
			name: hospital.name,
			address: hospital.address,
			fail: () => uni.showToast({ title: '无法打开地图', icon: 'none' }),
		})
	}

	function openArticle(article : Article) {
		uni.showModal({
			title: article.title,
			content: article.content || article.summary,
			confirmText: '记录相关提醒',
			cancelText: '关闭',
			success: (res) => {
				if (res.confirm) {
					uni.navigateTo({ url: '/pages/record/type-select' })
				}
			},
		})
	}

	function publishLostPet() {
		uni.showModal({
			title: '功能建设中',
			content: '丢失协寻需要先接入内容安全审核与附近推送。当前可先通过健康记录和宠物档案保存关键信息。',
			confirmText: '去宠物档案',
			cancelText: '关闭',
			success: (res) => {
				if (res.confirm) {
					uni.switchTab({ url: '/pages/index/index' })
				}
			},
		})
	}

	function normalizeKeyword(value : string) {
		return value.trim().toLowerCase()
	}

	function formatRating(value ?: number) {
		return value ? value.toFixed(1) : '暂无'
	}

	function formatDistance(value ?: number | null) {
		if (value == null || Number.isNaN(value)) return ''
		if (value < 1000) return `${Math.round(value)}m`
		return `${(value / 1000).toFixed(1)}km`
	}

	function calcDistance(lat1 : number, lng1 : number, lat2 : number, lng2 : number) {
		const radius = 6371000
		const radLat1 = toRadians(lat1)
		const radLat2 = toRadians(lat2)
		const deltaLat = toRadians(lat2 - lat1)
		const deltaLng = toRadians(lng2 - lng1)
		const a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2)
			+ Math.cos(radLat1) * Math.cos(radLat2)
			* Math.sin(deltaLng / 2) * Math.sin(deltaLng / 2)
		return radius * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
	}

	function toRadians(value : number) {
		return value * Math.PI / 180
	}

	function getCategoryStyle(category : string) {
		return categoryStyles[category] || categoryStyles.other
	}

	function getCategoryLabel(category : string) {
		return getCategoryStyle(category).label
	}

	function getArticleIcon(category : string) {
		return getCategoryStyle(category).icon
	}

	function getArticleCover(category : string) {
		return getCategoryStyle(category).cover
	}

	function getPetTypeLabel(type : string) {
		const map : Record<string, string> = { cat: '猫咪', dog: '狗狗', all: '通用' }
		return map[type] || '通用'
	}
</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);

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

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

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

	.hero-copy {
		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: 40rpx;
		font-weight: 900;
		color: #FFFFFF;
		line-height: 1.25;
	}

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

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

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

	.hero-stat-value {
		max-width: 176rpx;
		font-size: 31rpx;
		font-weight: 900;
		color: #FFFFFF;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}

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

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

	.search-card,
	.tab-card {
		margin: 20rpx 24rpx 0;
		padding: 6rpx;
		border-radius: 22rpx;
		background: $card-bg;
		box-shadow: $shadow;
		overflow: hidden;
	}

	.tab-card {
		display: grid;
		place-items: center;
		height: 80rpx;
		position: sticky;
		top: 0;
		z-index: 20;
	}

	.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;
	}

	.panel-section {
		padding: 26rpx 24rpx 0;
	}

	.section-head {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: 18rpx;
		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;
		line-height: 1.45;
	}

	.distance-filter {
		display: flex;
		gap: 12rpx;
		margin-bottom: 20rpx;
	}

	.distance-chip,
	.category-chip {
		min-height: 56rpx;
		box-sizing: border-box;
		display: flex;
		align-items: center;
		justify-content: center;
		gap: 8rpx;
		padding: 0 22rpx;
		border-radius: 999rpx;
		background: #FFFFFF;
		border: 1rpx solid $border;
		font-size: 24rpx;
		font-weight: 700;
		color: $text-body;
		transition: opacity 0.18s ease;

		&:active {
			opacity: 0.72;
		}
	}

	.distance-chip-active,
	.category-chip-active {
		background: $primary-bg;
		border-color: #FDBA74;
		color: $primary;
	}

	.loading-state,
	.empty-state {
		min-height: 420rpx;
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		gap: 18rpx;
	}

	.loading-text,
	.empty-desc {
		font-size: 25rpx;
		color: $text-muted;
		text-align: center;
		line-height: 1.55;
	}

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

	.hospital-list,
	.article-list {
		display: flex;
		flex-direction: column;
		gap: 16rpx;
	}

	.hospital-card,
	.article-card {
		position: relative;
		display: flex;
		align-items: center;
		gap: 18rpx;
		min-height: 150rpx;
		padding: 22rpx 20rpx 22rpx 24rpx;
		border-radius: 24rpx;
		background: $card-bg;
		box-shadow: $shadow;
		overflow: hidden;
		transition: transform 0.18s ease, box-shadow 0.18s ease;

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

	.hospital-accent {
		position: absolute;
		left: 0;
		top: 0;
		bottom: 0;
		width: 7rpx;
		background: linear-gradient(180deg, #F97316, #FDBA74);

		&.accent-open {
			background: linear-gradient(180deg, #0D9488, #2DD4BF);
		}
	}

	.hospital-main,
	.article-main {
		flex: 1;
		min-width: 0;
		display: flex;
		flex-direction: column;
		gap: 8rpx;
	}

	.hospital-title-row,
	.article-title-row {
		display: flex;
		align-items: center;
		gap: 12rpx;
		min-width: 0;
	}

	.hospital-name,
	.article-title {
		flex: 1;
		min-width: 0;
		font-size: 29rpx;
		font-weight: 850;
		color: $text-dark;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}

	.hospital-address,
	.article-summary {
		font-size: 24rpx;
		color: $text-muted;
		line-height: 1.45;
		overflow: hidden;
		text-overflow: ellipsis;
		display: -webkit-box;
		-webkit-line-clamp: 2;
		-webkit-box-orient: vertical;
	}

	.hospital-meta,
	.article-meta,
	.tag-row {
		display: flex;
		align-items: center;
		flex-wrap: wrap;
		gap: 9rpx;
	}

	.meta-pill {
		max-width: 190rpx;
		min-height: 38rpx;
		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;
		}
	}

	.urgent-pill {
		background: #ECFDF5;
		border-color: #CCFBF1;
		color: $teal;
		font-weight: 800;
	}

	.hospital-actions {
		display: flex;
		flex-direction: column;
		gap: 12rpx;
	}

	.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;
		}
	}

	.icon-button-disabled {
		background: #F8FAFC;
	}

	.category-scroll {
		width: 100%;
		white-space: nowrap;
		margin-bottom: 20rpx;
	}

	.category-track {
		display: inline-flex;
		gap: 12rpx;
		padding-right: 24rpx;
	}

	.article-cover {
		width: 96rpx;
		height: 96rpx;
		border-radius: 24rpx;
		display: flex;
		align-items: center;
		justify-content: center;
		flex-shrink: 0;
		box-shadow: 0 10rpx 22rpx rgba(15, 23, 42, 0.1);
	}

	.article-tags {
		flex: 1;
		min-width: 0;
		display: flex;
		gap: 10rpx;
		font-size: 21rpx;
		color: #94A3B8;

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

	.assist-card {
		display: flex;
		align-items: flex-start;
		gap: 20rpx;
		padding: 26rpx;
		border-radius: 24rpx;
		background: #FFFFFF;
		box-shadow: $shadow;
	}

	.assist-icon {
		width: 92rpx;
		height: 92rpx;
		border-radius: 26rpx;
		display: flex;
		align-items: center;
		justify-content: center;
		background: linear-gradient(135deg, #F97316, #FB923C);
		flex-shrink: 0;
	}

	.assist-copy,
	.step-copy {
		flex: 1;
		min-width: 0;
		display: flex;
		flex-direction: column;
		gap: 8rpx;
	}

	.assist-title,
	.step-title {
		font-size: 30rpx;
		font-weight: 850;
		color: $text-dark;
	}

	.assist-desc,
	.step-desc {
		font-size: 24rpx;
		line-height: 1.55;
		color: $text-muted;
	}

	.assist-steps {
		margin: 18rpx 0 26rpx;
		padding: 0 24rpx;
		border-radius: 24rpx;
		background: #FFFFFF;
		box-shadow: $shadow;
	}

	.step-row {
		display: flex;
		align-items: center;
		gap: 18rpx;
		padding: 24rpx 0;
		border-bottom: 1rpx solid #F1F5F9;

		&:last-child {
			border-bottom: 0;
		}
	}

	.step-icon {
		width: 64rpx;
		height: 64rpx;
		border-radius: 18rpx;
		background: $primary-bg;
		display: flex;
		align-items: center;
		justify-content: center;
		flex-shrink: 0;
	}

	.button-inner {
		display: flex;
		align-items: center;
		justify-content: center;
		gap: 9rpx;
	}
</style>
