<template>
  <view class="message-page">
    <!-- 顶部导航栏 - 使用自定义导航栏适配胶囊 -->
    <view class="nav-header" :style="navStyle">
      <view class="nav-left">
        <view class="back-btn" @tap="goBack">
          <text class="back-icon">‹</text>
        </view>
        <text class="nav-title">消息中心</text>
        <view v-if="unreadCount > 0" class="unread-tip">
          <text>{{ unreadCount }}</text>
        </view>
      </view>
      <view class="nav-right">
        <view 
          v-if="unreadCount > 0" 
          class="action-btn" 
          @tap="markAllAsRead"
          hover-class="action-btn-hover"
        >
          <text class="action-icon">✓</text>
          <text class="action-text">全部已读</text>
        </view>
      </view>
    </view>

    <!-- Tab 切换 -->
    <view class="tabs-wrapper" :style="tabsWrapperStyle">
      <scroll-view scroll-x class="tabs-scroll" :show-scrollbar="false">
        <view class="tabs-container">
          <view 
            v-for="tab in tabs" 
            :key="tab.value"
            :class="['tab-item', { active: currentTab === tab.value }]"
            @tap="switchTab(tab.value)"
          >
            <text class="tab-label">{{ tab.label }}</text>
            <view v-if="tab.value === 'unread' && unreadCount > 0" class="tab-badge">
              <text>{{ unreadCount > 99 ? '99+' : unreadCount }}</text>
            </view>
            <view v-if="currentTab === tab.value" class="tab-indicator"></view>
          </view>
        </view>
      </scroll-view>
    </view>

    <!-- 消息列表 -->
    <scroll-view 
      scroll-y 
      class="message-list" 
      :style="listStyle"
      :refresher-enabled="true"
      :refresher-triggered="refreshing"
      @refresherrefresh="onRefresh"
      @scrolltolower="onScrollToLower"
      refresher-background="#F5F7FA"
      refresher-default-style="black"
      lower-threshold="100"
    >
      <!-- 消息列表区域 -->
      <view class="message-list-content">
        <!-- 真实消息数据 -->
        <template v-if="groupedMessages.length > 0">
          <view v-for="(group, groupIndex) in groupedMessages" :key="groupIndex">
            <view class="group-date">
              <view class="group-date-line"></view>
              <text>{{ group.date }}</text>
              <view class="group-date-line"></view>
            </view>
            <view 
              v-for="message in group.items" 
              :key="message.id" 
              :class="['message-item', { read: message.read }]"
              @tap="openMessage(message)"
              hover-class="message-item-hover"
            >
              <view :class="['message-icon', message.type]">
                <image :src="getMessageIcon(message.type)" mode="aspectFit" class="icon-img" />
                <view v-if="!message.read" class="icon-dot">
                  <view class="dot-inner"></view>
                </view>
              </view>
              <view class="message-content">
                <view class="message-header">
                  <view class="title-wrapper">
                    <text class="message-title">{{ message.title }}</text>
                    <view v-if="!message.read" class="unread-tag">NEW</view>
                  </view>
                  <text class="message-time">{{ message.time }}</text>
                </view>
                <text class="message-desc">{{ message.content }}</text>
                <view class="message-footer">
                  <text class="message-type">{{ getTypeLabel(message.type) }}</text>
                  <view class="message-actions" v-if="!message.read">
                    <text class="action-mark" @tap.stop="markSingleAsRead(message)">标记已读</text>
                  </view>
                </view>
              </view>
              <view class="message-arrow">
                <text>›</text>
              </view>
            </view>
          </view>
        </template>
        
        <!-- 骨架屏项（混在列表中） -->
        <view v-for="i in skeletonCount" :key="'skeleton-' + i" class="message-item skeleton-item">
          <view class="message-icon skeleton-icon"></view>
          <view class="message-content">
            <view class="skeleton-title"></view>
            <view class="skeleton-desc"></view>
            <view class="skeleton-footer"></view>
          </view>
        </view>
      </view>
      
      <!-- 加载更多指示器 -->
      <view class="loading-more-state" v-if="loadingMore">
        <view class="loading-spinner small">
          <view class="spinner"></view>
        </view>
        <text class="loading-text">加载更多...</text>
      </view>

      <!-- 空状态 -->
      <view class="message-empty" v-if="filteredMessages.length === 0 && skeletonCount === 0 && !loading && !loadingMore">
        <view class="empty-icon-wrapper">
          <image src="https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/icons/message.png" class="empty-icon" />
        </view>
        <text class="empty-text">{{ emptyText }}</text>
        <text class="empty-desc">{{ emptyDesc }}</text>
        <view v-if="currentTab === 'unread'" class="empty-action" @tap="switchTab('all')">
          <text>查看全部消息 →</text>
        </view>
      </view>

      <!-- 已经到底啦 -->
      <view class="no-more" v-if="!hasMore && filteredMessages.length > 0 && !loadingMore">
        <text>已经到底啦~</text>
      </view>
    </scroll-view>
  </view>
</template>

<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { 
  getUserMessages, 
  getUnreadMessages, 
  getUnreadMessageCount,
  markMessageAsRead,
  markAllMessagesAsRead 
} from '@/api/main.js'

const messages = ref([])
const currentTab = ref('all')
const refreshing = ref(false)
const loading = ref(false)
const loadingMore = ref(false)
const hasMore = ref(true)
const currentPage = ref(0)
const pageSize = ref(10)
const skeletonCount = ref(10) // 骨架屏数量
const allRawMessages = ref([]) // 存储后端返回的所有原始数据
const TEST_MODE = false

// ============ 小程序胶囊适配 ============
const statusBarHeight = ref(20)
const navBarHeight = ref(44)
const menuButtonInfo = ref(null)

// 获取胶囊按钮信息
const getMenuButtonInfo = () => {
  // #ifdef MP-WEIXIN
  try {
    const info = uni.getMenuButtonBoundingClientRect()
    if (info) {
      menuButtonInfo.value = info
      return info
    }
  } catch (e) {
    console.warn('获取胶囊信息失败', e)
  }
  // #endif
  return null
}

// 获取系统信息
const getSystemInfo = () => {
  try {
    const info = uni.getSystemInfoSync()
    statusBarHeight.value = info.statusBarHeight || 20
    return info
  } catch (e) {
    return { statusBarHeight: 20 }
  }
}

// 初始化导航栏高度
const initNavBar = () => {
  const systemInfo = getSystemInfo()
  const menuButton = getMenuButtonInfo()
  
  if (menuButton) {
    const capsuleBottom = menuButton.top + menuButton.height
    navBarHeight.value = capsuleBottom + 8 - statusBarHeight.value
  } else {
    navBarHeight.value = 44
  }
}

// 导航栏样式
const navStyle = computed(() => {
  return {
    paddingTop: statusBarHeight.value + 'px',
    paddingRight: menuButtonInfo.value ? (menuButtonInfo.value.width + 12) + 'px' : '32rpx',
    minHeight: (statusBarHeight.value + navBarHeight.value) + 'px',
    height: (statusBarHeight.value + navBarHeight.value) + 'px'
  }
})

// Tab栏外层样式
const tabsWrapperStyle = computed(() => {
  const top = statusBarHeight.value + navBarHeight.value
  return {
    top: top + 'px'
  }
})

// 列表高度样式
const listStyle = computed(() => {
  const top = statusBarHeight.value + navBarHeight.value + 70
  return {
    height: `calc(100vh - ${top}px)`,
    paddingTop: '8rpx'
  }
})
// ============ 小程序胶囊适配结束 ============

const tabs = [
  { label: '全部', value: 'all' },
  { label: '未读', value: 'unread' },
  { label: '已读', value: 'read' }
]

// ============ 工具函数：时间戳字符串转数字 ============
/**
 * 将后端返回的时间戳字符串转换为数字
 * @param {string|number} timestamp - 时间戳字符串或数字
 * @returns {number} 数字类型的时间戳
 */
const parseTimestamp = (timestamp) => {
  if (!timestamp) return Date.now()
  
  // 如果是数字，直接返回
  if (typeof timestamp === 'number') {
    return timestamp
  }
  
  // 如果是字符串
  if (typeof timestamp === 'string') {
    // 尝试解析日期字符串格式，如 "2026-06-20 20:14:07"
    const dateStr = timestamp.replace(/-/g, '/').replace('T', ' ')
    const parsed = Date.parse(dateStr)
    if (!isNaN(parsed)) {
      return parsed
    }
    
    // 尝试转换为数字
    const num = Number(timestamp)
    if (isNaN(num)) {
      return Date.now()
    }
    
    // 如果是秒级时间戳（10位），转换为毫秒
    if (timestamp.length === 10) {
      return num * 1000
    }
    
    // 如果是毫秒级时间戳（13位），直接返回
    return num
  }
  
  return Date.now()
}
// ============ 工具函数结束 ============

// ============ 日期格式化 ============
/**
 * 格式化日期为 "6月17日 星期三" 或 "2025年6月17日 星期三" 格式
 * @param {string|number} timestamp - 时间戳字符串或数字
 * @returns {string} 格式化后的日期字符串
 */
const formatDate = (timestamp) => {
  // 解析时间戳
  const ts = parseTimestamp(timestamp)
  const date = new Date(ts)
  const now = new Date()
  
  // 检查日期是否有效
  if (isNaN(date.getTime())) {
    return '未知日期'
  }
  
  const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
  const messageDate = new Date(date.getFullYear(), date.getMonth(), date.getDate())
  
  const diffDays = Math.floor((today - messageDate) / (1000 * 60 * 60 * 24))
  
  // 月份、日期、星期
  const month = date.getMonth() + 1
  const day = date.getDate()
  const year = date.getFullYear()
  const currentYear = now.getFullYear()
  const weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
  const weekday = weekdays[date.getDay()]
  
  // 判断是否为今年
  const isThisYear = year === currentYear
  
  if (diffDays === 0) {
    return '今天'
  } else if (diffDays === 1) {
    return '昨天'
  } else {
    // 2天及以上显示日期
    if (isThisYear) {
      // 今年：显示 "6月17日 星期三"
      return `${month}月${day}日 ${weekday}`
    } else {
      // 不是今年：显示 "2025年6月17日 星期三"
      return `${year}年${month}月${day}日 ${weekday}`
    }
  }
}

/**
 * 格式化时间为 "HH:MM" 格式
 * @param {string|number} timestamp - 时间戳字符串或数字
 * @returns {string} 格式化后的时间字符串
 */
const formatTime = (timestamp) => {
  // 解析时间戳
  const ts = parseTimestamp(timestamp)
  const date = new Date(ts)
  
  // 检查日期是否有效
  if (isNaN(date.getTime())) {
    return '--:--'
  }
  
  const hours = String(date.getHours()).padStart(2, '0')
  const minutes = String(date.getMinutes()).padStart(2, '0')
  return `${hours}:${minutes}`
}
// ============ 日期格式化结束 ============

// ============ 消息类型映射 ============
/**
 * 将后端消息类型映射为前端显示类型
 * @param {string} type - 后端消息类型
 * @returns {string} 前端显示类型
 */
const mapMessageType = (type) => {
  const typeMap = {
    'COURSE': 'course',
    'SYSTEM': 'system',
    'ACTIVITY': 'activity',
    'POINTS': 'points',
    'course': 'course',
    'system': 'system',
    'activity': 'activity',
    'points': 'points'
  }
  return typeMap[type] || 'system'
}

/**
 * 将后端消息数据转换为前端格式
 * @param {object} msg - 后端消息对象
 * @returns {object} 前端消息对象
 */
const transformMessage = (msg) => {
  console.log('[transformMessage] 原始消息:', msg.id, 'createTime:', msg.createTime, 'createdAt:', msg.createdAt)
  return {
    id: msg.id,
    type: mapMessageType(msg.type),
    title: msg.title,
    content: msg.content,
    timestamp: msg.createTime || msg.createdAt || Date.now(),
    read: msg.isRead === true || msg.isRead === 1 || msg.isRead === '1'
  }
}
// ============ 消息类型映射结束 ============

// ============ 测试模式假数据 ============
const mockTemplates = [
  { type: 'course', title: '课程开始提醒', content: '您预约的「{course}」课程将在10分钟后开始，请提前到达训练场地。' },
  { type: 'course', title: '课程取消通知', content: '抱歉，您预约的「{course}」课程因教练临时有事已取消。' },
  { type: 'course', title: '课程预约成功', content: '您已成功预约「{course}」课程，请准时参加。' },
  { type: 'activity', title: '新活动上线', content: '「{activity}」火热开启，参与即有机会赢取健身礼包！' },
  { type: 'activity', title: '活动报名成功', content: '恭喜您成功报名「{activity}」，期待您的参与！' },
  { type: 'activity', title: '活动即将开始', content: '您报名的「{activity}」将在明天开始，请做好准备。' },
  { type: 'points', title: '积分到账通知', content: '您的运动积分已到账 +{points}，累计积分可兑换精美礼品。' },
  { type: 'points', title: '积分兑换成功', content: '您已成功兑换「{gift}」，请在有效期内使用。' },
  { type: 'points', title: '积分即将过期', content: '您有 {points} 积分将于月底过期，请尽快使用。' },
  { type: 'system', title: '系统维护通知', content: '系统将于今晚22:00-24:00进行维护升级，期间可能影响部分功能使用。' },
  { type: 'system', title: '账户安全提醒', content: '检测到您的账户在新设备上登录，如非本人操作请及时修改密码。' },
  { type: 'system', title: '密码修改成功', content: '您的账户密码已成功修改，如非本人操作请联系客服。' }
]

const mockCourses = ['晨间活力唤醒跑', '全身力量塑形', '瑜伽放松课', '动感单车', '拳击有氧', '普拉提核心', 'HIIT燃脂', '游泳训练']
const mockActivities = ['夏日减脂挑战赛', '端午健身派对', '会员感恩周', '新人打卡活动', '周末运动嘉年华', '健身达人挑战赛']
const mockGifts = ['周卡体验券', '月卡优惠券', '私教体验课', '运动水壶', '健身手套', '蛋白粉试用装']

const generateMockMessages = (page, size) => {
  // 模拟3页数据，每页10条，共30条
  const maxPages = 3
  if (page >= maxPages) {
    return [] // 模拟到底
  }
  
  const messages = []
  for (let i = 0; i < size; i++) {
    const template = mockTemplates[Math.floor(Math.random() * mockTemplates.length)]
    let content = template.content
    
    // 替换占位符
    content = content.replace('{course}', mockCourses[Math.floor(Math.random() * mockCourses.length)])
    content = content.replace('{activity}', mockActivities[Math.floor(Math.random() * mockActivities.length)])
    content = content.replace('{points}', Math.floor(Math.random() * 200 + 50))
    content = content.replace('{gift}', mockGifts[Math.floor(Math.random() * mockGifts.length)])
    
    // 生成时间戳：当前时间往前推随机天数
    const daysAgo = page * size + i + Math.floor(Math.random() * 3)
    const timestamp = Date.now() - daysAgo * 86400000 - Math.floor(Math.random() * 3600000)
    
    messages.push({
      id: `mock_${page}_${i}`,
      type: template.type,
      title: template.title,
      content: content,
      createdAt: timestamp,
      isRead: Math.random() > 0.4 // 60%概率未读
    })
  }
  
  return messages
}
// ============ 测试模式假数据结束 ============

const unreadCount = computed(() => {
  if (propsUnreadCount.value !== null && propsUnreadCount.value !== undefined) {
    return propsUnreadCount.value
  }
  return messages.value.filter(msg => !msg.read).length
})

const filteredMessages = computed(() => {
  if (currentTab.value === 'all') {
    return messages.value
  } else if (currentTab.value === 'unread') {
    return messages.value.filter(msg => !msg.read)
  } else if (currentTab.value === 'read') {
    return messages.value.filter(msg => msg.read)
  }
  return messages.value
})

const emptyText = computed(() => {
  if (currentTab.value === 'unread') return '🎉 暂无未读消息'
  if (currentTab.value === 'read') return '📭 暂无已读消息'
  return '📬 暂无消息'
})

const emptyDesc = computed(() => {
  if (currentTab.value === 'unread') return '您已读完所有消息，继续保持哦！'
  if (currentTab.value === 'read') return '还没有已读的消息，快去查看吧！'
  return '您还没有收到任何通知'
})

const groupedMessages = computed(() => {
  const processedMessages = filteredMessages.value.map(msg => ({
    ...msg,
    date: formatDate(msg.timestamp),
    time: formatTime(msg.timestamp)
  }))
  
  const groups = {}
  processedMessages.forEach(msg => {
    if (!groups[msg.date]) {
      groups[msg.date] = []
    }
    groups[msg.date].push(msg)
  })
  
  // 自定义排序：今天 > 昨天 > 其他日期（按时间倒序）
  const dateOrder = ['今天', '昨天']
  const sortedDates = Object.keys(groups).sort((a, b) => {
    const indexA = dateOrder.indexOf(a)
    const indexB = dateOrder.indexOf(b)
    
    if (indexA !== -1 && indexB !== -1) {
      return indexA - indexB
    }
    if (indexA !== -1) return -1
    if (indexB !== -1) return 1
    
    // 按日期倒序排列
    // 从日期字符串中提取日期进行比较
    const dateA = new Date(a.replace(/[年月日]/g, '/'))
    const dateB = new Date(b.replace(/[年月日]/g, '/'))
    return dateB - dateA
  })
  
  return sortedDates.map(date => ({
    date,
    items: groups[date]
  }))
})

const getMessageIcon = (type) => {
  const icons = {
    course: 'https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/icons/course.png',
    system: 'https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/icons/plan.png',
    activity: 'https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/icons/data.png',
    points: 'https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/icons/message.png'
  }
  return icons[type] || icons.system
}

const getTypeLabel = (type) => {
  const labels = {
    course: '课程通知',
    system: '系统消息',
    activity: '活动推送',
    points: '积分动态'
  }
  return labels[type] || '通知'
}

const goBack = () => {
  uni.reLaunch({
    url: '/pages/index/index'
  })
}

const switchTab = (tab) => {
  if (currentTab.value === tab) return
  currentTab.value = tab
  if (uni.vibrateShort) {
    uni.vibrateShort({
      type: 'light'
    })
  }
}

const openMessage = async (message) => {
  uni.showLoading({
    title: '加载中...',
    mask: true
  })
  
  const startTime = Date.now()
  
  try {
    if (!message.read && !TEST_MODE) {
      await markMessageAsRead(message.id)
      message.read = true
      updateUnreadCount()
    } else if (!message.read && TEST_MODE) {
      message.read = true
      updateUnreadCount()
    }
  } catch (e) {
    console.error('标记已读失败:', e)
  } finally {
    const elapsed = Date.now() - startTime
    const minDelay = 300
    if (elapsed < minDelay) {
      await new Promise(resolve => setTimeout(resolve, minDelay - elapsed))
    }
    uni.hideLoading()
  }
  
  if (uni.vibrateShort) {
    uni.vibrateShort({
      type: 'light'
    })
  }
  const messageData = encodeURIComponent(JSON.stringify(message))
  uni.navigateTo({
    url: `/pages/message/detail?message=${messageData}`
  })
}

const markSingleAsRead = async (message) => {
  if (TEST_MODE) {
    message.read = true
    updateUnreadCount()
    uni.showToast({
      title: '已标记为已读',
      icon: 'success',
      duration: 1200
    })
    return
  }
  
  try {
    await markMessageAsRead(message.id)
    message.read = true
    updateUnreadCount()
    uni.showToast({
      title: '已标记为已读',
      icon: 'success',
      duration: 1200
    })
  } catch (e) {
    console.error('标记已读失败:', e)
    uni.showToast({
      title: '操作失败',
      icon: 'none',
      duration: 1200
    })
  }
}

const markAllAsRead = () => {
  uni.showModal({
    title: '确认操作',
    content: '确定要将所有未读消息标记为已读吗？',
    confirmColor: '#FF6B35',
    success: async (res) => {
      if (res.confirm) {
        if (TEST_MODE) {
          messages.value.forEach(msg => {
            if (!msg.read) {
              msg.read = true
            }
          })
          updateUnreadCount()
          uni.showToast({
            title: '已全部标为已读',
            icon: 'success',
            duration: 1500
          })
          return
        }
        
        try {
          const userId = uni.getStorageSync('userId') || 1
          console.log('请求标记全部已读, userId:', userId)
          const res = await markAllMessagesAsRead(userId)
          console.log('标记全部已读响应数据:', res)
          messages.value.forEach(msg => {
            if (!msg.read) {
              msg.read = true
            }
          })
          updateUnreadCount()
          uni.showToast({
            title: '已全部标为已读',
            icon: 'success',
            duration: 1500
          })
        } catch (e) {
          console.error('全部标记已读失败:', e)
          uni.showToast({
            title: '操作失败',
            icon: 'none',
            duration: 1500
          })
        }
      }
    }
  })
}

const onRefresh = async () => {
  refreshing.value = true
  currentPage.value = 0
  hasMore.value = true
  try {
    await loadMessages(true)
    uni.showToast({
      title: '刷新成功',
      icon: 'success',
      duration: 1000
    })
  } catch (e) {
    console.error('刷新失败:', e)
    uni.showToast({
      title: '刷新失败',
      icon: 'none',
      duration: 1000
    })
  } finally {
    refreshing.value = false
  }
}

const onScrollToLower = () => {
  if (loadingMore.value || !hasMore.value || loading.value) return
  loadMore()
}

const loadMore = async () => {
  // 检查是否可以加载更多
  if (!hasMore.value) {
    console.log('没有更多数据了')
    return
  }
  
  if (loading.value) {
    console.log('正在加载中，跳过')
    return
  }
  
  if (loadingMore.value) {
    console.log('正在加载更多中，跳过')
    return
  }
  
  console.log('触发加载更多，当前页:', currentPage.value, '已有消息数:', messages.value.length)
  
  loadingMore.value = true
  // 加载更多时添加骨架屏（用户滑多少就显示多少个）
  skeletonCount.value += pageSize.value
  
  try {
    await loadMessages(false)
  } catch (e) {
    console.error('加载更多失败:', e)
  } finally {
    loadingMore.value = false
  }
}

const updateUnreadCount = () => {
  const count = messages.value.filter(msg => !msg.read).length
  try {
    uni.setStorageSync('unreadMessageCount', count)
  } catch (e) {
    console.error('保存未读消息数失败:', e)
  }
}

// ============ 加载消息数据 ============

/**
 * 加载消息列表
 * @param {boolean} isRefresh - 是否刷新（true=重置数据，false=追加数据）
 */
const loadMessages = async (isRefresh = true) => {
  // 防止重复请求
  if (loading.value) return
  
  loading.value = true
  
  // 处理骨架屏：初始加载时显示骨架屏
  if (isRefresh) {
    skeletonCount.value = pageSize.value
    messages.value = []
    allRawMessages.value = []
    currentPage.value = 0
    hasMore.value = true
  }
  // 加载更多时，skeletonCount 已在 loadMore 中累加，这里不需要重置
  
  // 记录开始时间，用于计算最小延迟
  const startTime = Date.now()
  const minDelay = 1500 // 最小延迟 1500ms，确保骨架屏可见
  
  try {
    if (TEST_MODE) {
      // 模拟网络延迟，确保骨架屏可见
      await new Promise(resolve => setTimeout(resolve, 2000))
      
      const mockData = generateMockMessages(currentPage.value, pageSize.value)
      const processedMessages = mockData.map(transformMessage)
      
      // 模拟后端分页结构
      const mockRes = {
        content: mockData,
        totalPages: 3,
        totalElements: 30,
        currentPage: currentPage.value,
        pageSize: pageSize.value,
        first: currentPage.value === 0,
        last: currentPage.value >= 2 || mockData.length < pageSize.value
      }
      console.log('[测试模式] 分页响应:', mockRes)
      
      // 等待最小延迟后再添加数据（确保骨架屏显示）
      const elapsed = Date.now() - startTime
      if (elapsed < minDelay) {
        await new Promise(resolve => setTimeout(resolve, minDelay - elapsed))
      }
      
      if (processedMessages.length > 0) {
        // 一次性添加所有数据
        messages.value.push(...processedMessages)
        // 清空骨架屏
        skeletonCount.value = 0
        
        // 使用 last 判断是否有更多数据
        hasMore.value = !mockRes.last
        currentPage.value++
        
        updateUnreadCount()
      } else {
        // 没有数据时，清空剩余骨架屏
        skeletonCount.value = 0
        hasMore.value = false
      }
      return
    }
    
    const userId = uni.getStorageSync('userId') || 1
    const params = { page: currentPage.value, size: pageSize.value }
    console.log('请求消息列表, userId:', userId, 'params:', params)
    const res = await getUserMessages(userId, params)
    console.log('消息列表响应数据:', res)
    
    // 解析后端响应
    let content = []
    let last = true
    let isPaged = false // 标记后端是否返回分页结构
    
    if (res && Array.isArray(res)) {
      // 直接返回数组（兼容旧接口）
      content = res
    } else if (res && res.content && Array.isArray(res.content)) {
      // 标准分页结构
      content = res.content
      last = res.last === true
      isPaged = true
    } else if (res && res.data && res.data.content && Array.isArray(res.data.content)) {
      // 嵌套在 data 中的分页结构
      content = res.data.content
      last = res.data.last === true
      isPaged = true
    } else if (res && res.data && Array.isArray(res.data)) {
      // 直接返回数组在 data 中
      content = res.data
    }
    
    // 等待最小延迟后再添加数据（确保骨架屏显示）
    const elapsed = Date.now() - startTime
    if (elapsed < minDelay) {
      await new Promise(resolve => setTimeout(resolve, minDelay - elapsed))
    }
    
    // 判断后端是否真的分页：如果返回的数据量大于 pageSize，说明后端返回了所有数据
    const backendReturnedAll = content.length > pageSize.value
    
    if ((!isPaged || backendReturnedAll) && content.length > 0) {
      // 后端返回了所有数据（或没有分页结构），前端自己做分页
      if (allRawMessages.value.length === 0) {
        allRawMessages.value = content
        console.log('后端返回所有数据，共', content.length, '条，前端将做分页切片')
      }
      
      // 计算当前页应该显示的数据
      const start = currentPage.value * pageSize.value
      const end = start + pageSize.value
      const pageData = allRawMessages.value.slice(start, end)
      
      console.log('前端分页:', { start, end, pageDataLength: pageData.length, total: allRawMessages.value.length })
      
      const newMessages = pageData.map(transformMessage)
      
      if (newMessages.length > 0) {
        // 一次性添加所有数据
        messages.value.push(...newMessages)
        // 清空骨架屏
        skeletonCount.value = 0
        
        // 判断是否还有更多（前端分页）
        hasMore.value = end < allRawMessages.value.length
        currentPage.value++
        
        updateUnreadCount()
      } else {
        // 当前页没有数据，清空骨架屏
        skeletonCount.value = 0
        hasMore.value = false
      }
    } else {
      // 后端支持分页，直接使用返回的数据
      const newMessages = content.map(transformMessage)
      
      if (newMessages.length > 0) {
        // 一次性添加所有数据
        messages.value.push(...newMessages)
        // 清空骨架屏
        skeletonCount.value = 0
        
        // 使用 last 判断是否有更多数据
        hasMore.value = !last
        currentPage.value++
        
        updateUnreadCount()
      } else {
        // 没有数据时，清空剩余骨架屏
        skeletonCount.value = 0
        hasMore.value = false
      }
    }
  } catch (e) {
    console.error('获取消息列表失败:', e)
    skeletonCount.value = 0
    if (isRefresh) {
      try {
        const cachedMessages = uni.getStorageSync('cachedMessages')
        if (cachedMessages && Array.isArray(cachedMessages)) {
          messages.value = cachedMessages
        }
      } catch (e2) {
        console.error('读取缓存消息失败:', e2)
      }
    }
  } finally {
    loading.value = false
    loadingMore.value = false
  }
}

/**
 * 加载未读消息数量
 */
const loadUnreadCount = async () => {
  // 测试模式下不请求后端
  if (TEST_MODE) {
    return
  }
  
  try {
    const userId = uni.getStorageSync('userId') || 1
    console.log('请求未读消息数量, userId:', userId)
    const res = await getUnreadMessageCount(userId)
    console.log('未读消息数量响应数据:', res)
    if (res && typeof res === 'number') {
      uni.setStorageSync('unreadMessageCount', res)
    } else if (res && typeof res.count === 'number') {
      uni.setStorageSync('unreadMessageCount', res.count)
    } else if (res && res.data && typeof res.data === 'number') {
      uni.setStorageSync('unreadMessageCount', res.data)
    } else if (res && res.data && typeof res.data.count === 'number') {
      uni.setStorageSync('unreadMessageCount', res.data.count)
    }
  } catch (e) {
    console.error('获取未读消息数失败:', e)
  }
}
// ============ 加载消息数据结束 ============

const propsUnreadCount = ref(null)

onLoad((options) => {
  if (options?.unreadCount) {
    propsUnreadCount.value = parseInt(options.unreadCount, 10) || null
    console.log('[Message] 从URL接收未读数量:', propsUnreadCount.value)
  }
  loadMessages()
})

onMounted(() => {
  initNavBar()
})

onUnmounted(() => {
  uni.$emit('pageShow')
})
</script>

<style lang="scss" scoped>
.message-page {
  min-height: 100vh;
  background: #F5F7FA;
}

// ============ 导航栏 ============
.nav-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-left: 32rpx;
  padding-right: 32rpx;
  background: var(--bg-white);
  border-bottom: 1rpx solid rgba(0, 0, 0, 0.04);
  position: sticky;
  top: 0;
  z-index: 20;
  box-sizing: border-box;
  
  .nav-left {
    display: flex;
    align-items: center;
    gap: 12rpx;
    
    .back-btn {
      width: 56rpx;
      height: 56rpx;
      display: flex;
      align-items: center;
      justify-content: center;
      border-radius: 50%;
      background: rgba(0, 0, 0, 0.03);
      transition: all 0.2s ease;
      flex-shrink: 0;
      
      .back-icon {
        font-size: 48rpx;
        color: var(--text-dark);
        font-weight: 300;
        line-height: 1;
        margin-top: -4rpx;
      }
      
      &:active {
        background: rgba(0, 0, 0, 0.06);
        transform: scale(0.92);
      }
    }
    
    .nav-title {
      font-size: 38rpx;
      font-weight: 700;
      color: var(--text-dark);
      letter-spacing: 0.5rpx;
      white-space: nowrap;
    }
    
    .unread-tip {
      display: flex;
      align-items: center;
      justify-content: center;
      min-width: 36rpx;
      height: 36rpx;
      padding: 0 12rpx;
      background: rgba(255, 71, 87, 0.12);
      border-radius: 18rpx;
      border: 1rpx solid rgba(255, 71, 87, 0.1);
      flex-shrink: 0;
      
      text {
        font-size: 22rpx;
        color: #FF4757;
        font-weight: 600;
        line-height: 1;
        text-align: center;
      }
    }
  }

  .nav-right {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    
    .action-btn {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 6rpx;
      padding: 10rpx 24rpx;
      height: 56rpx;
      background: linear-gradient(135deg, rgba(255, 107, 53, 0.12), rgba(255, 107, 53, 0.06));
      border-radius: 28rpx;
      transition: all 0.2s ease;
      border: 1rpx solid rgba(255, 107, 53, 0.1);
      box-sizing: border-box;
      
      .action-icon {
        font-size: 22rpx;
        color: var(--accent-orange);
        font-weight: 700;
      }
      
      .action-text {
        font-size: 24rpx;
        color: var(--accent-orange);
        font-weight: 500;
        white-space: nowrap;
      }
    }
    
    .action-btn-hover {
      opacity: 0.7;
      transform: scale(0.95);
    }
  }
}

// ============ Tab 栏 ============
.tabs-wrapper {
  position: sticky;
  z-index: 10;
  background: var(--bg-white);
  border-bottom: 1rpx solid rgba(0, 0, 0, 0.04);
}

.tabs-scroll {
  white-space: nowrap;
  
  .tabs-container {
    display: inline-flex;
    padding: 0 32rpx;
    height: 70rpx;
    align-items: center;
    gap: 8rpx;
  }
}

.tab-item {
  display: inline-flex;
  align-items: center;
  padding: 0 28rpx;
  height: 56rpx;
  position: relative;
  border-radius: 28rpx;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  
  .tab-label {
    font-size: 26rpx;
    color: var(--text-muted);
    transition: all 0.3s ease;
    font-weight: 500;
  }
  
  .tab-badge {
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 28rpx;
    height: 28rpx;
    padding: 0 8rpx;
    margin-left: 6rpx;
    background: linear-gradient(135deg, #FF4757, #FF2D55);
    border-radius: 14rpx;
    box-shadow: 0 4rpx 12rpx rgba(255, 71, 87, 0.3);
    animation: badgePop 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
    
    text {
      font-size: 18rpx;
      color: white;
      font-weight: 700;
      line-height: 1;
      text-align: center;
    }
  }
  
  .tab-indicator {
    position: absolute;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%) scaleX(0);
    width: 28rpx;
    height: 4rpx;
    border-radius: 2rpx;
    background: linear-gradient(90deg, #FF6B35, #FF8A5C);
    transition: all 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
  }
  
  &.active {
    .tab-label {
      color: var(--accent-orange);
      font-weight: 600;
      font-size: 28rpx;
    }
    
    .tab-indicator {
      transform: translateX(-50%) scaleX(1);
      width: 36rpx;
    }
  }
  
  &:active {
    opacity: 0.7;
  }
}

@keyframes badgePop {
  0% { transform: scale(0); opacity: 0; }
  60% { transform: scale(1.3); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}

// ============ 消息列表 ============
.message-list {
  background: #F5F7FA;
}

.message-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 200rpx 48rpx;

  .empty-icon-wrapper {
    width: 200rpx;
    height: 200rpx;
    background: linear-gradient(135deg, rgba(120, 185, 215, 0.06), rgba(120, 185, 215, 0.12));
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 40rpx;
    transition: all 0.3s ease;
    animation: float 3s ease-in-out infinite;
    
    .empty-icon {
      width: 120rpx;
      height: 120rpx;
      opacity: 0.5;
    }
  }

  @keyframes float {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-12rpx); }
  }

  .empty-text {
    font-size: 32rpx;
    font-weight: 600;
    color: var(--text-muted);
    margin-bottom: 12rpx;
  }

  .empty-desc {
    font-size: 26rpx;
    color: var(--text-light);
  }
  
  .empty-action {
    margin-top: 32rpx;
    padding: 12rpx 32rpx;
    background: rgba(255, 107, 53, 0.08);
    border-radius: 30rpx;
    
    text {
      font-size: 26rpx;
      color: var(--accent-orange);
      font-weight: 500;
    }
    
    &:active {
      opacity: 0.7;
    }
  }
}

.group-date {
  padding: 24rpx 32rpx 16rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 20rpx;
  
  .group-date-line {
    flex: 0 0 60rpx;
    height: 1rpx;
    background: linear-gradient(to right, transparent, rgba(0,0,0,0.06), transparent);
  }

  text {
    font-size: 24rpx;
    color: var(--text-light);
    background: rgba(0, 0, 0, 0.04);
    padding: 6rpx 24rpx;
    border-radius: 20rpx;
    letter-spacing: 1rpx;
    font-weight: 500;
  }
}

.message-item {
  display: flex;
  align-items: center;
  padding: 24rpx 28rpx;
  margin: 0 24rpx 12rpx;
  background: var(--bg-white);
  border-radius: 16rpx;
  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
  transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
  position: relative;
  border: 1rpx solid rgba(0, 0, 0, 0.03);

  &.message-item-hover {
    transform: scale(0.98);
    background: #F8FAFB;
    box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
  }

  &.read {
    .message-title {
      color: var(--text-muted);
      font-weight: 400;
    }
    
    .message-desc {
      color: var(--text-light);
    }
    
    .message-icon {
      filter: grayscale(0.3);
      opacity: 0.7;
    }
  }

  .message-icon {
    width: 80rpx;
    height: 80rpx;
    border-radius: 20rpx;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-right: 20rpx;
    flex-shrink: 0;
    position: relative;
    box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
    transition: all 0.3s ease;

    &.course {
      background: linear-gradient(135deg, #4A90D9 0%, #2E6BB5 100%);
    }

    &.system {
      background: linear-gradient(135deg, #52C41A 0%, #389E0D 100%);
    }

    &.activity {
      background: linear-gradient(135deg, #FF6B35 0%, #E85D2C 100%);
    }

    &.points {
      background: linear-gradient(135deg, #667EEA 0%, #5A4BD1 100%);
    }

    .icon-img {
      width: 40rpx;
      height: 40rpx;
    }
    
    .icon-dot {
      position: absolute;
      top: -4rpx;
      right: -4rpx;
      width: 20rpx;
      height: 20rpx;
      border-radius: 50%;
      background: linear-gradient(135deg, #FF4757, #FF2D55);
      border: 2.5rpx solid white;
      display: flex;
      align-items: center;
      justify-content: center;
      animation: pulse 2s ease-in-out infinite;
      box-shadow: 0 0 0 3rpx rgba(255, 71, 87, 0.15);
      
      .dot-inner {
        width: 6rpx;
        height: 6rpx;
        border-radius: 50%;
        background: white;
        opacity: 0.9;
      }
    }
  }

  @keyframes pulse {
    0%, 100% { 
      transform: scale(1);
      box-shadow: 0 0 0 3rpx rgba(255, 71, 87, 0.15);
    }
    50% { 
      transform: scale(1.1);
      box-shadow: 0 0 0 6rpx rgba(255, 71, 87, 0.08);
    }
  }

  .message-content {
    flex: 1;
    min-width: 0;

    .message-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 6rpx;
      
      .title-wrapper {
        display: flex;
        align-items: center;
        gap: 8rpx;
        flex: 1;
        min-width: 0;

        .message-title {
          font-size: 28rpx;
          font-weight: 600;
          color: var(--text-dark);
          white-space: nowrap;
          overflow: hidden;
          text-overflow: ellipsis;
        }
        
        .unread-tag {
          flex-shrink: 0;
          font-size: 16rpx;
          color: #FF4757;
          background: rgba(255, 71, 87, 0.08);
          padding: 2rpx 10rpx;
          border-radius: 8rpx;
          font-weight: 700;
          animation: tagPop 0.3s ease;
          border: 1rpx solid rgba(255, 71, 87, 0.08);
          letter-spacing: 0.5rpx;
          line-height: 1.4;
        }
      }

      .message-time {
        font-size: 22rpx;
        color: var(--text-light);
        flex-shrink: 0;
        margin-left: 12rpx;
        font-weight: 400;
      }
    }

    .message-desc {
      font-size: 26rpx;
      color: var(--text-muted);
      display: -webkit-box;
      -webkit-line-clamp: 2;
      -webkit-box-orient: vertical;
      overflow: hidden;
      line-height: 1.5;
      margin-bottom: 8rpx;
    }
    
    .message-footer {
      display: flex;
      align-items: center;
      justify-content: space-between;
      
      .message-type {
        font-size: 20rpx;
        color: var(--text-light);
        opacity: 0.6;
        background: rgba(0, 0, 0, 0.03);
        padding: 2rpx 12rpx;
        border-radius: 8rpx;
      }
      
      .message-actions {
        .action-mark {
          font-size: 20rpx;
          color: var(--accent-orange);
          opacity: 0.7;
          padding: 2rpx 12rpx;
          background: rgba(255, 107, 53, 0.06);
          border-radius: 8rpx;
          
          &:active {
            opacity: 0.4;
          }
        }
      }
    }
  }
  
  .message-arrow {
    margin-left: 12rpx;
    flex-shrink: 0;
    width: 36rpx;
    height: 36rpx;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.02);
    transition: all 0.2s ease;
    
    text {
      color: var(--text-light);
      font-size: 28rpx;
      opacity: 0.25;
      font-weight: 300;
    }
  }
}

@keyframes tagPop {
  0% { transform: scale(0.5); opacity: 0; }
  70% { transform: scale(1.1); }
  100% { transform: scale(1); opacity: 1; }
}

/* 骨架屏项样式 */
.skeleton-item {
  .skeleton-icon {
    background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
    background-size: 200% 100%;
    animation: shimmer 1.5s infinite;
  }
  
  .skeleton-title {
    height: 32rpx;
    width: 60%;
    background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
    background-size: 200% 100%;
    animation: shimmer 1.5s infinite;
    border-radius: 8rpx;
    margin-bottom: 16rpx;
  }
  
  .skeleton-desc {
    height: 28rpx;
    width: 85%;
    background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
    background-size: 200% 100%;
    animation: shimmer 1.5s infinite;
    border-radius: 8rpx;
    margin-bottom: 16rpx;
  }
  
  .skeleton-footer {
    height: 24rpx;
    width: 40%;
    background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
    background-size: 200% 100%;
    animation: shimmer 1.5s infinite;
    border-radius: 8rpx;
  }
}

@keyframes shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* 加载更多状态 */
.loading-more-state {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 30rpx 0;
  gap: 16rpx;
  
  .loading-spinner {
    width: 36rpx;
    height: 36rpx;
    
    .spinner {
      width: 100%;
      height: 100%;
      border: 4rpx solid #f3f3f3;
      border-top: 4rpx solid #FF6B35;
      border-radius: 50%;
      animation: spin 1s linear infinite;
    }
    
    &.small {
      width: 32rpx;
      height: 32rpx;
    }
  }
  
  .loading-text {
    font-size: 26rpx;
    color: #94a3b8;
  }
}

/* 已经到底啦 */
.no-more {
  padding: 32rpx 0;
  text-align: center;
  
  text {
    font-size: 24rpx;
    color: #94a3b8;
  }
}

@keyframes spin {
  to { transform: rotate(360deg); }
}
</style>