<template>
  <scroll-view class="waterfall-scroll" :style="scrollStyle" scroll-x :show-scrollbar="false"
    :scroll-left="isIOS ? undefined : scrollLeft" :bounce="false" @scroll="onScroll" @touchstart="onTouchStart"
    @touchend="onTouchEnd" @touchcancel="onTouchEnd">
    <view ref="waterfallInnerRef" :class="{ 'waterfall-inner': true, 'ios-css-animate': !iosScrollPaused }"
      :style="innerStyle">
      <block v-for="(card, index) in loopedCards" :key="card.id + '_' + index">
        <view v-if="card && !card._blank" class="wf-card" :style="cardStyle(card)"
          @click="handleCardClick(card)">
          <!-- 渐变边框光效 -->
          <view class="wf-card-border" :style="borderStyle(card)" />

          <!-- 封面图 -->
          <image v-if="card.coverUrl" class="wf-card-img" :src="card.coverUrl" mode="aspectFill"
            :style="{ width: card.w + 'px', height: card.h + 'px', borderRadius: card.radius + 'px' }" />

          <!-- 光波动画层 - 外层 -->
          <view class="wf-like-wave wf-like-wave-outer" :class="{ 'wf-like-wave-active': likingMap[card.id] }"
            :style="{ width: card.w + 'px', height: card.h + 'px', borderRadius: card.radius + 'px' }" />
          <!-- 光波动画层 - 内层 -->
          <view class="wf-like-wave wf-like-wave-inner" :class="{ 'wf-like-wave-active': likingMap[card.id] }"
            :style="{ width: card.w + 'px', height: card.h + 'px', borderRadius: card.radius + 'px' }" />

          <!-- 底部点赞数 -->
          <view class="wf-card-footer">
            <image class="wf-heart" src="/static/icon/heart-icon.png" mode="aspectFit" />
            <view class="wf-likes-wrap">
              <text class="wf-likes">{{ formatLikes(card.likes) }}</text>
            </view>
          </view>
        </view>
      </block>
    </view>
  </scroll-view>
</template>

<script setup>
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
import { getInspirationFlowApi, getBatchOssPresignedUrlsApi } from '@/utils/api.js'
import { doubleTapLike } from '@/utils/likeHelper.js'
import { calcSpan } from '../config/mockData.js'

const props = defineProps({
  screenWidth: { type: Number, default: 375 },
  screenHeight: { type: Number, default: 812 },
  bannerBottom: { type: Number, default: 200 },
  useMockData: { type: Boolean, default: false },  // 是否使用模拟数据
  category: { type: String, default: '' },  // 当前分类
  isActive: { type: Boolean, default: true },  // 是否处于激活状态
})

const emit = defineEmits(['cardClick', 'scroll'])

// ========== 布局常量 ==========
const rpx2px = (rpx) => Math.round(uni.getSystemInfoSync().windowWidth / 750 * rpx)
const GAP = rpx2px(24)
const BORDER_W = rpx2px(2)
const SCALE = 0.9
const ROWS = 4
const AUTO_SCROLL_SPEED_ANDROID = 0.3
const AUTO_SCROLL_SPEED_IOS = 0.015
const AUTO_RESUME_DELAY = 1500
const PRELOAD_THRESHOLD = rpx2px(300)

// ========== 状态 ==========
const cards = ref([])
const allUsers = ref([])
const totalWidth = ref(0)
const scrollLeft = ref(0)
const likingMap = ref({})  // 记录正在播放点赞动画的卡片ID
let currentScrollLeft = 0
let idCounter = 0
let isComponentMounted = false  // 标记组件是否已卸载
let appendFailed = false  // 标记追加是否已失败
let isInitialLoading = true  // 标记是否在初始加载中
let isIOS = false  // 是否为 iOS 平台
let cursor = ''  // 游标分页（右侧新数据）
let leftCursor = ''  // 游标分页（左侧历史数据）
let leftAppendFailed = false  // 标记左侧追加是否已失败

// ========== RAF 兼容 ==========
const rafFn = (cb) => {
  if (typeof requestAnimationFrame !== 'undefined') return requestAnimationFrame(cb)
  if (uni.requestAnimationFrame) return uni.requestAnimationFrame(cb)
  return setTimeout(cb, 16)
}
const cafFn = (id) => {
  if (typeof cancelAnimationFrame !== 'undefined') return cancelAnimationFrame(id)
  if (uni.cancelAnimationFrame) return uni.cancelAnimationFrame(id)
  clearTimeout(id)
}

// ========== iOS CSS 动画自动滚动 ==========
// iOS 使用 CSS @keyframes 动画实现流畅滚动，不再用 setInterval + scroll-left
const iosScrollPaused = ref(true)

const startIOSAutoScroll = () => {
  if (!isComponentMounted || !isIOS) return
  iosScrollPaused.value = false

  // 启动 iOS 滚动位置追踪，定时发送 scrollLeft 给父组件
  clearInterval(iosScrollEmitTimer)
  iosScrollEmitTimer = setInterval(() => {
    if (!isComponentMounted) return
    // iOS CSS 动画从 0 到 totalWidth.value，循环播放
    const scrollDist = totalWidth.value
    const duration = scrollDist / AUTO_SCROLL_SPEED_IOS
    const elapsed = (Date.now() % duration)
    const pos = (elapsed / duration) * scrollDist
    emit('scroll', pos)

    // iOS 预加载检查（CSS 动画不触发 onScroll，所以在这里检查）
    const remainingScroll = scrollDist - pos - props.screenWidth
    if (remainingScroll < Math.max(scrollDist / 2, props.screenWidth)) {
      if (!isLoadingMore && !appendFailed && !isInitialLoading) {
        scheduleAppend()
      }
    }
  }, 16)
}

const stopIOSAutoScroll = () => {
  iosScrollPaused.value = true
  clearInterval(iosScrollEmitTimer)
  iosScrollEmitTimer = null
}

const pauseIOSAutoScroll = () => {
  iosScrollPaused.value = true
  clearInterval(iosScrollEmitTimer)
  iosScrollEmitTimer = null
}

const resumeIOSAutoScroll = () => {
  iosScrollPaused.value = false
  // 重新启动 iOS 滚动位置追踪
  clearInterval(iosScrollEmitTimer)
  iosScrollEmitTimer = setInterval(() => {
    if (!isComponentMounted) return
    const scrollDist = totalWidth.value
    const duration = scrollDist / AUTO_SCROLL_SPEED_IOS
    const elapsed = (Date.now() % duration)
    const pos = (elapsed / duration) * scrollDist
    emit('scroll', pos)
  }, 16)
}
let rafId = null
let userInteracting = false
let resumeTimer = null
let appendTimer = null  // 防抖定时器
let autoScrollPos = 0  // 自动滚动目标位置
let momentumTimer = null  // iOS/Android 惯性滚动检测定时器
let scrollUpdateTimer = null  // 定期同步 scrollLeft 的定时器（仅 Android）
let iosScrollEmitTimer = null  // iOS 滚动位置发射定时器
let iosCurrentScrollPos = 0  // iOS CSS 动画当前滚动位置

const startAutoScroll = () => {
  if (!isComponentMounted || isIOS) return
  if (rafId) {
    cafFn(rafId)
    rafId = null
  }
  userInteracting = false
  autoScrollPos = currentScrollLeft

  // 使用定时器定期更新 scrollLeft
  clearInterval(scrollUpdateTimer)
  scrollUpdateTimer = setInterval(() => {
    if (!isComponentMounted || userInteracting) return
    autoScrollPos += AUTO_SCROLL_SPEED_ANDROID
    if (autoScrollPos >= totalWidth.value) {
      autoScrollPos = 0
    }
    scrollLeft.value = autoScrollPos

    // 预加载检查
    const remainingScroll = totalWidth.value - autoScrollPos - props.screenWidth
    if (remainingScroll < props.screenWidth) {
      if (!isLoadingMore && !appendFailed && !isInitialLoading) {
        appendMore()
      }
    }
  }, 16)
}

const stopAutoScroll = () => {
  if (rafId) {
    cafFn(rafId)
    rafId = null
  }
  clearInterval(scrollUpdateTimer)
  scrollUpdateTimer = null
  clearTimeout(appendTimer)
  appendTimer = null
  clearTimeout(resumeTimer)
  resumeTimer = null
  clearTimeout(momentumTimer)
  momentumTimer = null
  userInteracting = false
  isLoadingMore = false
}

// 检测 Android 惯性滚动是否结束
let lastTouchEndPos = 0  // touchend 时的位置快照

const detectMomentumEnd = () => {
  clearTimeout(momentumTimer)
  lastTouchEndPos = currentScrollLeft
  const tick = () => {
    momentumTimer = setTimeout(() => {
      // 比较的是 lastTouchEndPos（touchend 时的位置），不受后续惯性影响
      if (Math.abs(currentScrollLeft - lastTouchEndPos) < 3) {
        // 安卓：恢复自动滚动
        autoScrollPos = currentScrollLeft
        startAutoScroll()
      } else {
        // 惯性中，更新快照
        lastTouchEndPos = currentScrollLeft
        tick()
      }
    }, 60)
  }
  tick()
}

// 防抖：延迟追加
const scheduleAppend = () => {
  if (!isComponentMounted || appendFailed) return
  if (appendTimer) clearTimeout(appendTimer)
  appendTimer = setTimeout(() => {
    appendTimer = null
    if (isComponentMounted && !appendFailed) {
      appendMore()
    }
  }, 800)
}

// ========== 触摸 / 滚动事件 ==========
let isManualScrolling = false  // 是否正在手动滚动

const onTouchStart = (e) => {
  if (isIOS) {
    // iOS 设备：暂停 CSS 动画，让用户可以自由滚动
    pauseIOSAutoScroll()
    isManualScrolling = true
    clearTimeout(momentumTimer)
  } else {
    // 安卓设备：自动滚动停止
    userInteracting = true
    clearTimeout(momentumTimer)
    clearInterval(scrollUpdateTimer)
  }
}

const onTouchEnd = () => {
  if (isIOS) {
    // iOS 设备：检测惯性滚动是否结束
    clearTimeout(momentumTimer)
    lastTouchEndPos = currentScrollLeft
    const tick = () => {
      momentumTimer = setTimeout(() => {
        if (Math.abs(currentScrollLeft - lastTouchEndPos) < 2) {
          // 惯性真正结束，等 500ms 确保惯性完全停止后再恢复
          clearTimeout(momentumTimer)
          momentumTimer = setTimeout(() => {
            isManualScrolling = false
            resumeIOSAutoScroll()
          }, 1500)
        } else {
          // 惯性中，更新快照继续检测
          lastTouchEndPos = currentScrollLeft
          tick()
        }
      }, 80)
    }
    tick()
  } else {
    // 安卓设备：检测惯性滚动是否结束
    detectMomentumEnd()
  }
}

const onScroll = (e) => {
  if (!isComponentMounted) return
  currentScrollLeft = e.detail.scrollLeft

  // 发送滚动事件给父组件用于背景视差
  emit('scroll', currentScrollLeft)

  // iOS 手动滚动时，不再更新自动滚动相关变量
  if (isIOS && isManualScrolling) {
    return
  }

  const remainingScroll = totalWidth.value - currentScrollLeft - props.screenWidth
  // 向左滚动到底触发追加新数据
  if (remainingScroll < props.screenWidth && currentScrollLeft > 0) {
    if (!isLoadingMore && !appendFailed && !isInitialLoading) {
      scheduleAppend()
    }
  }
  // 向右滚动触发追加历史数据
  if (currentScrollLeft > props.screenWidth && leftCursor) {
    if (!isLoadingMore && !leftAppendFailed && !isInitialLoading) {
      appendLeft()
    }
  }
}

// ========== 样式 ==========
const scrollStyle = computed(() => ({
  position: 'absolute',
  top: props.bannerBottom + 'px',
  left: 0,
  width: props.screenWidth + 'px',
  height: (props.screenHeight - props.bannerBottom) + 'px',
  zIndex: 2,
  overflow: 'hidden',
}))

// iOS CSS 动画内联样式
// 无缝循环需要两份内容拼接：[原内容|原内容]
// 动画从 0 滚动到 -totalWidth，当原内容滚动到末端时，第二份正好接上
const innerStyle = computed(() => {
  if (!isIOS) {
    return { width: (totalWidth.value * 2) + 'px', height: '100%' }
  }
  const scrollDist = totalWidth.value
  // 速度：px/ms，iOS 使用 AUTO_SCROLL_SPEED_IOS
  const duration = scrollDist / AUTO_SCROLL_SPEED_IOS
  return {
    width: (scrollDist * 2) + 'px',
    height: '100%',
    '--scroll-dist': -scrollDist + 'px',
    '--anim-duration': duration + 'ms',
    '--play-state': iosScrollPaused.value ? 'paused' : 'running',
    // 强制内联样式控制 animation-play-state
    animationPlayState: iosScrollPaused.value ? 'paused' : 'running',
  }
})

// 获取循环后的卡片列表（用于渲染两份内容实现无缝循环）
const loopedCards = computed(() => {
  if (!cards.value || cards.value.length === 0) return []
  // 返回两份内容，无缝循环
  return [...cards.value, ...cards.value.map(card => ({ ...card }))]
})

// ========== 布局引擎 ==========
// 核心思路：4×4 网格装箱，按块放置（每块16格），优先竖形状，放不下换横，最后填空白
// span 对应面积：
//   span 1: 1×1 = 1格
//   span 2: 优先 1×2(竖) 或 2×1(横) = 2格
//   span 3: 2×2 = 4格
//   span 4: 优先 2×3(竖) 或 3×2(横) = 6格
//   span 5: 3×3 = 9格
// 注意：所有图片都是 4:3 比例
class WaterfallLayout {
  constructor(containerW, containerH, gap = GAP) {
    this.containerW = containerW
    this.containerH = containerH
    this.category = 'hot'  // 默认分类
    this.gap = gap
    this.COLS = 4
    this.ROWS = 4
    this.BLOCK_SIZE = this.COLS * this.ROWS  // 16格
    this.IMG_RATIO = 4 / 3  // 图片宽高比 4:3

    // 计算每格尺寸（预留 gap）- 基于 4:3 图片比例计算
    // cellW 是基准宽度，cellH 根据图片比例计算以保持 4:3
    const totalGapW = gap * (this.COLS - 1)
    const totalGapH = gap * (this.ROWS - 1)
    this.cellW = Math.floor((containerW - totalGapW) / this.COLS)
    this.cellH = Math.floor(this.cellW * 3 / 4)  // 保持 4:3 比例

    // 实际行高可能不足以填满容器，调整
    const usedH = this.cellH * this.ROWS + totalGapH
    if (usedH > containerH) {
      // 高度超出，压缩 cellH
      this.cellH = Math.floor((containerH - totalGapH) / this.ROWS)
    }
  }

  // span → 形状列表（优先竖的）
  // 竖 = 高 > 宽（占列多），横 = 宽 > 高（占行多）
  _getShapes(span) {
    switch (span) {
      case 1: return [{ w: 1, h: 1, vertical: false }]
      case 2: return [
        { w: 1, h: 2, vertical: true },   // 竖：窄长
        { w: 2, h: 1, vertical: false }   // 横：扁宽
      ]
      case 3: return [{ w: 2, h: 2, vertical: false }]
      case 4: return [
        { w: 2, h: 3, vertical: true },   // 竖：高>宽
        { w: 3, h: 2, vertical: false }   // 横：宽>高
      ]
      case 5: return [{ w: 3, h: 3, vertical: false }]
      default: return [{ w: 1, h: 1, vertical: false }]
    }
  }

  // 点赞数 → span
  _calcSpan(likes) {
    return calcSpan(this.category, likes)
  }

  // 创建空网格
  _createGrid() {
    return Array(this.ROWS).fill(null).map(() => Array(this.COLS).fill(0))
  }

  // 检查是否能放入
  _canPlace(grid, row, col, w, h) {
    if (row + h > this.ROWS || col + w > this.COLS) return false
    for (let r = row; r < row + h; r++) {
      for (let c = col; c < col + w; c++) {
        if (grid[r][c] !== 0) return false
      }
    }
    return true
  }

  // 标记占用
  _markGrid(grid, row, col, w, h) {
    for (let r = row; r < row + h; r++) {
      for (let c = col; c < col + w; c++) {
        grid[r][c] = 1
      }
    }
  }

  // 计算网格中可用格子数量
  _countAvailableCells(grid) {
    let count = 0
    for (let r = 0; r < this.ROWS; r++) {
      for (let c = 0; c < this.COLS; c++) {
        if (grid[r][c] === 0) count++
      }
    }
    return count
  }

  // 找空白位置（列优先：先左右，再上下）
  _findSpace(grid, w, h) {
    for (let c = 0; c <= this.COLS - w; c++) {
      for (let r = 0; r <= this.ROWS - h; r++) {
        if (this._canPlace(grid, r, c, w, h)) {
          return { row: r, col: c }
        }
      }
    }
    return null
  }

  // 填空白格（列优先：先左右，再上下）
  _fillBlanks(grid, placed, baseX, baseY) {
    for (let c = 0; c < this.COLS; c++) {
      for (let r = 0; r < this.ROWS; r++) {
        if (grid[r][c] === 0) {
          placed.push({
            id: idCounter++,
            left: baseX + c * (this.cellW + this.gap),
            top: baseY + r * (this.cellH + this.gap),
            w: this.cellW,
            h: this.cellH,
            _blank: true,
            radius: 8,
          })
          grid[r][c] = 1
        }
      }
    }
  }

  // 放置一块（4×4 = 16格）
  _placeBlock(items, blockIndex) {
    const grid = this._createGrid()
    const placed = []
    const baseX = blockIndex * (this.COLS * (this.cellW + this.gap) + this.gap)
    const baseY = 0

    // 计算可放置卡片数量的上限（考虑每5个会强制留空）
    const maxCardsPerBlock = this.BLOCK_SIZE  // 16格

    let consecutiveSpan1 = 0

    for (const item of items) {
      const span = item.span != null ? item.span : this._calcSpan(item.likes || 0)

      // 检查是否还有空间
      const availableCells = this._countAvailableCells(grid)
      if (availableCells < 1) {
        // 没有空间了，跳过这个卡片
        continue
      }

      // span1 连续放置 5 个后，先留 1-2 个空白格再继续
      if (span === 1 && consecutiveSpan1 >= 5) {
        const blanks = Math.random() < 0.5 ? 1 : 2
        for (let b = 0; b < blanks; b++) {
          const pos = this._findSpace(grid, 1, 1)
          if (pos) {
            this._markGrid(grid, pos.row, pos.col, 1, 1)
            placed.push({
              id: idCounter++,
              left: baseX + pos.col * (this.cellW + this.gap),
              top: baseY + pos.row * (this.cellH + this.gap),
              w: this.cellW,
              h: this.cellH,
              _blank: true,
              radius: 8,
            })
          }
        }
        consecutiveSpan1 = 0  // 重置计数
      }

      const shape = this._getShapes(span)[0]  // 只用第一个形状
      let success = false

      // 优先第一个形状，找不到就全网格搜索能放的位置
      if (shape) {
        const pos = this._findSpace(grid, shape.w, shape.h)
        if (pos) {
          this._markGrid(grid, pos.row, pos.col, shape.w, shape.h)
          const left = baseX + pos.col * (this.cellW + this.gap)
          const top = baseY + pos.row * (this.cellH + this.gap)
          const w = shape.w * this.cellW + (shape.w - 1) * this.gap
          const h = shape.h * this.cellH + (shape.h - 1) * this.gap
          placed.push({ ...item, left, top, w, h, radius: 8 })
          success = true
        }

        // 第一个形状放不下，全网格搜索其他可用的位置
        if (!success) {
          for (let c = 0; c <= this.COLS - shape.w && !success; c++) {
            for (let r = 0; r <= this.ROWS - shape.h && !success; r++) {
              if (grid[r][c] === 0) {
                this._markGrid(grid, r, c, shape.w, shape.h)
                const left = baseX + c * (this.cellW + this.gap)
                const top = baseY + r * (this.cellH + this.gap)
                const w = shape.w * this.cellW + (shape.w - 1) * this.gap
                const h = shape.h * this.cellH + (shape.h - 1) * this.gap
                placed.push({ ...item, left, top, w, h, radius: 8 })
                success = true
              }
            }
          }
        }
      }

      // 如果还是放不下，强制找到一个空位放置（避免卡片丢失）
      if (!success) {
        for (let r = 0; r < this.ROWS && !success; r++) {
          for (let c = 0; c < this.COLS && !success; c++) {
            if (grid[r][c] === 0) {
              this._markGrid(grid, r, c, 1, 1)
              const left = baseX + c * (this.cellW + this.gap)
              const top = baseY + r * (this.cellH + this.gap)
              placed.push({ ...item, left, top, w: this.cellW, h: this.cellH, radius: 8 })
              success = true
            }
          }
        }
      }

      if (!success) {
        console.warn('[WaterfallLayout] 无法放置卡片 span=' + span)
      }

      if (span === 1 && success) {
        consecutiveSpan1++
      }
    }

    // 填空白
    this._fillBlanks(grid, placed, baseX, baseY)
    return placed
  }

  // 计算全部卡片布局
  compute(users) {
    this.curX = 0
    const result = []

    // 每 16 张卡片为一块
    for (let i = 0; i < users.length; i += this.BLOCK_SIZE) {
      const block = users.slice(i, i + this.BLOCK_SIZE)
      const blockResult = this._placeBlock(block, Math.floor(i / this.BLOCK_SIZE))
      result.push(...blockResult)
    }

    this.totalWidth = (Math.ceil(users.length / this.BLOCK_SIZE)) *
      (this.COLS * (this.cellW + this.gap) + this.gap)
    return result
  }

  // 追加更多卡片（追加到下一块）
  addCards(users) {
    const result = []
    const blockIndex = Math.floor(allUsers.value.length / this.BLOCK_SIZE)
    const blockResult = this._placeBlock(users, blockIndex)
    result.push(...blockResult)

    // 计算新块的宽度（基于该块中所有卡片的最右边位置）
    let blockMaxX = 0
    for (const card of blockResult) {
      const right = card.left + card.w
      if (right > blockMaxX) {
        blockMaxX = right
      }
    }
    // 加上最后一块的 gap，得到该块的真实宽度
    const blockWidth = blockMaxX + this.gap

    // 累积总宽度
    this.totalWidth += blockWidth
    return result
  }

  getTotalWidth() {
    return this.totalWidth || 0
  }
}

let layout = null

// ========== 颜色主题 ==========
const PALETTES = [
  'linear-gradient(135deg, #2d1b69, #5b21b6)',
  'linear-gradient(135deg, #0c4a6e, #0284c7)',
  'linear-gradient(135deg, #450a0a, #b91c1c)',
  'linear-gradient(135deg, #064e3b, #059669)',
  'linear-gradient(135deg, #78350f, #d97706)',
  'linear-gradient(135deg, #4a1942, #be185d)',
]

const BORDER_COLORS = [
  'linear-gradient(135deg, #a78bfa, #60a5fa)',
  'linear-gradient(135deg, #38bdf8, #818cf8)',
  'linear-gradient(135deg, #f472b6, #fb923c)',
  'linear-gradient(135deg, #4ade80, #22d3ee)',
  'linear-gradient(135deg, #fbbf24, #f87171)',
  'linear-gradient(135deg, #c084fc, #f472b6)',
]

// ========== 样式函数 ==========
const cardStyle = (card) => ({
  position: 'absolute',
  left: card.left + 'px',
  top: card.top + 'px',
  width: card.w + 'px',
  height: card.h + 'px',
  borderRadius: card.radius + 'px',
  // overflow: 'hidden',
  background: card.coverUrl ? 'transparent' : PALETTES[Math.abs(card.id) % PALETTES.length],
})

const borderStyle = (card) => ({
  position: 'absolute',
  borderRadius: (card.radius + BORDER_W) + 'px',
  background: BORDER_COLORS[Math.abs(card.id) % BORDER_COLORS.length],
  inset: `-${BORDER_W}px`,
  zIndex: 0,
  opacity: 0.85,
})

const formatLikes = (n) => {
  if (n >= 10000) return (n / 10000).toFixed(1) + 'w'
  if (n >= 1000) return (n / 1000).toFixed(1) + 'k'
  return String(n)
}

// ========== 数据加载 ==========
let isLoadingMore = false

// 后端不返回 likes 时，前端随机模拟
const randomLikes = () => {
  const r = Math.random()
  if (r < 0.20) return Math.floor(Math.random() * 100)
  if (r < 0.40) return Math.floor(100 + Math.random() * 400)
  if (r < 0.60) return Math.floor(500 + Math.random() * 1500)
  if (r < 0.75) return Math.floor(2000 + Math.random() * 6000)
  if (r < 0.90) return Math.floor(8000 + Math.random() * 42000)
  return Math.floor(50000 + Math.random() * 950000)
}

// 本地素材图，循环使用
const MOCK_IMAGES = Array.from({ length: 16 }, (_, i) => `/static/sucai/image-${String(i + 1).padStart(2, '0')}.png`)

const mapUser = (u) => {
  // 直接使用后端返回的图片URL
  let coverUrl = u.cover_url || MOCK_IMAGES[idCounter % MOCK_IMAGES.length]
  // 后端可以返回 span 字段（1~4）来控制卡片大小，不传则前端随机模板决定
  return {
    id: idCounter++,
    userId: u.user_id,
    nickname: u.owner_nickname || u.nickname,
    coverUrl,
    likes: u.like_count ?? u.likes ?? randomLikes(),
    span: u.span ?? null,  // 后端控制字段，null = 前端随机模板决定
  }
}

// 批量获取预签名URL
const batchGetPresignedUrls = async (urls) => {
  if (!urls || urls.length === 0) return {}
  try {
    const files = urls.filter(u => u)
    const res = await getBatchOssPresignedUrlsApi(files, 3600, 'asset')
    if (res?.code === 0 && res.data?.files) {
      // 构建 key -> presigned_url 的映射
      const map = {}
      for (const file of res.data.files) {
        map[file.key] = file.presigned_url
      }
      return map
    }
  } catch (_) { }
  return {}
}

const loadUsers = async () => {
  if (!isComponentMounted) return Promise.resolve()

  // 切换分类时重置
  cursor = ''
  isLoadingMore = true

  try {
    // 使用真实API
    const res = await getInspirationFlowApi({ limit: 20, type: props.category, direction: 'right', cursor })
    if (!isComponentMounted) return
    if (res.code === 0 && res.data?.items && res.data.items.length > 0) {
      const items = res.data.items
      // 保存游标用于下次分页请求
      cursor = res.data.cursor || ''

      const withData = items.map((item) => {
        return {
          id: item.asset_id,
          exhibition_id: item.exhibition_id,
          userId: item.asset_id,
          nickname: item.owner_nickname || item.name,
          coverUrl: item.cover_url || MOCK_IMAGES[idCounter % MOCK_IMAGES.length],
          likes: item.likes || item.like_count || 0,
          span: item.span ?? null,
        }
      })

      allUsers.value = withData
      cards.value = layout.compute(withData)
      totalWidth.value = layout.getTotalWidth()
    } else {
      // 接口没数据
      appendFailed = true
    }
    isLoadingMore = false
  } catch (e) {
    console.error('[WaterfallGrid] 加载用户失败', e?.message ?? e)
    isLoadingMore = false
  }
}

const appendMore = async () => {
  if (!isComponentMounted) {
    return
  }
  if (isLoadingMore) {
    return
  }
  isLoadingMore = true
  try {
    const res = await getInspirationFlowApi({ limit: 20, type: props.category, direction: 'right', cursor })
    if (!isComponentMounted) return
    if (res.code === 0 && res.data?.items && res.data.items.length > 0) {
      const items = res.data.items
      cursor = res.data.cursor || ''

      const withData = items.map((item) => {
        return {
          id: item.asset_id,
          userId: item.asset_id,
          nickname: item.owner_nickname || item.name,
          coverUrl: item.cover_url || MOCK_IMAGES[idCounter % MOCK_IMAGES.length],
          likes: item.likes || item.like_count || 0,
          span: item.span ?? null,
        }
      })

      appendFailed = false
      const placed = layout.addCards(withData)
      console.log('[appendMore success] totalWidth:', totalWidth.value, 'isLoadingMore:', isLoadingMore)

      cards.value = [...cards.value, ...placed]
      allUsers.value = [...allUsers.value, ...withData]

      // totalWidth 已在 addCards 中更新

      // 保持滚动位置连续性
      if (!isIOS) {
        stopAutoScroll()
        // 重启时从当前位置开始，但确保不会立即触发下一次加载
        // 向前移动一点距离，避免刚好在触发阈值上
        autoScrollPos = currentScrollLeft + 100
        if (autoScrollPos >= totalWidth.value) {
          autoScrollPos = 0
        }
        startAutoScroll()
      }
    } else {
      // 没有可追加的数据，标记失败停止
      appendFailed = true
    }
  } catch (e) {
    console.error('[WaterfallGrid] 追加用户失败', e?.message ?? e)
    appendFailed = true
  } finally {
    isLoadingMore = false
  }
}

// 追加左侧历史数据（向右滚动查看）
const appendLeft = async () => {
  if (!isComponentMounted) return
  if (isLoadingMore) return
  isLoadingMore = true
  try {
    const res = await getInspirationFlowApi({ limit: 20, type: props.category, direction: 'left', cursor: leftCursor })
    if (!isComponentMounted) return
    if (res.code === 0 && res.data?.items && res.data.items.length > 0) {
      const items = res.data.items
      leftCursor = res.data.cursor || ''

      const withData = items.map((item) => {
        return {
          id: item.asset_id + '_left',
          userId: item.asset_id,
          nickname: item.owner_nickname || item.name,
          coverUrl: item.cover_url || MOCK_IMAGES[idCounter % MOCK_IMAGES.length],
          likes: item.likes || item.like_count || 0,
          span: item.span ?? null,
        }
      })

      const placed = layout.addCards(withData)
      cards.value = [...placed, ...cards.value]
      allUsers.value = [...withData, ...allUsers.value]
      totalWidth.value = layout.getTotalWidth()

      scrollLeft.value = currentScrollLeft + placed.reduce((sum, card) => sum + card.w + GAP, 0)

      if (!isIOS) {
        stopAutoScroll()
        startAutoScroll()
      }
    } else {
      leftCursor = ''
    }
  } catch (e) {
    console.error('[WaterfallGrid] 追加左侧历史数据失败', e?.message ?? e)
  } finally {
    isLoadingMore = false
  }
}

// ========== 点击处理（双击点赞，单击跳转） ==========
const cardTapTimers = {};

const handleCardClick = (card) => {

  if (cardTapTimers[card.id]) {
    // 第二次点击，双击点赞
    clearTimeout(cardTapTimers[card.id]);
    delete cardTapTimers[card.id];


    // 触发动画
    likingMap.value = { ...likingMap.value, [card.id]: true };
    setTimeout(() => {
      likingMap.value = { ...likingMap.value, [card.id]: false };
    }, 600);

    doubleTapLike(card.id, card.exhibition_id || 0, (success) => {
      if (success) {
        // 找到 cards 中的卡片并更新 likes，触发响应式更新
        const idx = cards.value.findIndex(c => c.id === card.id)
        if (idx !== -1) {
          cards.value[idx].likes = (cards.value[idx].likes || 0) + 1
          // 强制触发更新
          cards.value = [...cards.value]
        }
        uni.showToast({ title: '点赞成功', icon: 'success' })
      }
    });
  } else {
    // 第一次点击，单击跳转
    if (card.id) {
      cardTapTimers[card.id] = setTimeout(() => {
        delete cardTapTimers[card.id];
        uni.navigateTo({ url: `/pages/asset-detail/asset-detail?asset_id=${card.id}` });
      }, 300);
    }
  }
}

// ========== 初始化 ==========
onMounted(() => {
  isComponentMounted = true
  isInitialLoading = true
  appendFailed = false
  userInteracting = false
  currentScrollLeft = 0
  scrollLeft.value = 0
  // 获取设备平台信息，iOS 不使用自动滚动
  const sysInfo = uni.getSystemInfoSync()
  isIOS = sysInfo.platform === 'ios'
  const containerH = props.screenHeight - props.bannerBottom
  layout = new WaterfallLayout(props.screenWidth, containerH, GAP)
  // 监听点赞事件，实时更新点赞数
  uni.$on('assetLiked', ({ asset_id, data }) => {
    const idx = cards.value.findIndex(c => c.id === asset_id)
    if (idx !== -1 && data && typeof data.likes === 'number') {
      cards.value[idx].likes = data.likes
      // 强制触发响应式更新
      cards.value = [...cards.value]
    }
  })
  loadUsers().then(() => {
    isInitialLoading = false
    nextTick(() => {
      if (isIOS) {
        startIOSAutoScroll()
      } else {
        startAutoScroll()
      }
    })
  })
})

// ========== 可见性控制 ==========
const stopAllAutoScroll = () => {
  stopAutoScroll()
  stopIOSAutoScroll()
}

const resumeAllAutoScroll = () => {
  // 清理所有残留的滚动/惯性定时器，确保干净启动
  clearTimeout(momentumTimer)
  momentumTimer = null
  stopAllAutoScroll()
  if (!isComponentMounted) return
  // 使用 scrollLeft.value 而非 currentScrollLeft，因为手动滚动时
  // currentScrollLeft 不更新（onScroll 在 isManualScrolling 时直接 return）
  autoScrollPos = scrollLeft.value
  if (isIOS) {
    startIOSAutoScroll()
  } else {
    startAutoScroll()
  }
}

// H5 页面可见性监听
const handleVisibilityChange = () => {
  if (!isComponentMounted) return
  if (document.hidden) {
    stopAllAutoScroll()
  } else {
    // 页面从隐藏恢复时，momentum 已在 stopAllAutoScroll 中清掉，不需要等待
    resumeAllAutoScroll()
  }
}

// uni-app 生命周期：App 进入前台/后台
let appShowListener = null
let appHideListener = null

// iOS/Android 小程序可见性监听（通过页面 onShow/onHide）
// uni-app 生命周期：App 进入前台/后台
const onAppShowHandler = () => {
  if (!isComponentMounted) return
  // 页面从隐藏恢复时，momentum 已在 stopAllAutoScroll 中清掉，不需要等待
  // 直接 resume，iOS 靠自己的 RAF 驱动，Android 靠 setInterval
  resumeAllAutoScroll()
}

const onAppHideHandler = () => {
  if (!isComponentMounted) return
  stopAllAutoScroll()
}

if (typeof uni !== 'undefined') {
  uni.onAppShow(onAppShowHandler)
  uni.onAppHide(onAppHideHandler)
}

// H5 环境额外监听 visibilitychange
if (typeof document !== 'undefined') {
  document.addEventListener('visibilitychange', handleVisibilityChange)
}

// 供父组件调用的可见性控制方法（通过 defineExpose 暴露）
const handleAppShow = () => {
  if (!isComponentMounted) return
  setTimeout(() => {
    resumeAllAutoScroll()
  }, AUTO_RESUME_DELAY)
}

const handleAppHide = () => {
  if (!isComponentMounted) return
  stopAllAutoScroll()
}

// 暴露方法给父组件
defineExpose({ handleAppShow, handleAppHide })

// 监听 isActive 属性变化（父组件控制）
watch(() => props.isActive, (active) => {
  if (!isComponentMounted) return
  if (active) {
    stopAllAutoScroll()
    setTimeout(() => {
      resumeAllAutoScroll()
    }, 150)
  } else {
    stopAllAutoScroll()
  }
}, { immediate: false })

onUnmounted(() => {
  isComponentMounted = false
  stopAllAutoScroll()
  clearTimeout(resumeTimer)
  clearTimeout(appendTimer)
  clearTimeout(momentumTimer)
  if (typeof document !== 'undefined') {
    document.removeEventListener('visibilitychange', handleVisibilityChange)
  }
  // 移除 uni-app 全局监听
  if (typeof uni !== 'undefined') {
    try {
      uni.offAppShow(onAppShowHandler)
      uni.offAppHide(onAppHideHandler)
      uni.off('assetLiked')
    } catch (_) { }
  }
})

watch(() => [props.screenHeight, props.bannerBottom], () => {
  const containerH = props.screenHeight - props.bannerBottom
  layout = new WaterfallLayout(containerH, props.category)
  if (allUsers.value.length) {
    cards.value = layout.compute(allUsers.value)
    totalWidth.value = layout.getTotalWidth()
  }
})

// 监听分类变化，重新加载数据
watch(() => props.category, (newCategory) => {
  if (isComponentMounted) {
    stopAllAutoScroll()

    // 取消待执行的追加
    if (appendTimer) {
      clearTimeout(appendTimer)
      appendTimer = null
    }

    // 重置滚动位置
    currentScrollLeft = 0
    scrollLeft.value = 0
    cursor = ''
    leftCursor = ''
    leftAppendFailed = false

    // 重新创建布局（使用新的 span 阈值）
    const containerH = props.screenHeight - props.bannerBottom
    layout = new WaterfallLayout(containerH, newCategory)
    cards.value = []
    allUsers.value = []
    totalWidth.value = 0
    appendFailed = false
    isInitialLoading = false

    // 先停止 RAF，确保在 loadUsers 完成前不会有滚动逻辑
    if (rafId) {
      cafFn(rafId)
      rafId = null
    }

    // 父组件通过 key 变化来重置组件，这里不需要额外处理
    // 延迟加载数据
    setTimeout(() => {
      loadUsersAndStartScroll()
    }, 0)
  }
})

const loadUsersAndStartScroll = () => {
  loadUsers().then(() => {
    nextTick(() => {
      if (isIOS) {
        // iOS 启动自动滚动
        iosScrollPaused.value = false
      } else {
        startAutoScroll()
      }
    })
  })
}
</script>

<style scoped>
.waterfall-scroll {
  white-space: nowrap;
  overflow: hidden;
}

.waterfall-inner {
  position: relative;
  height: 100%;
  display: inline-block;
  top: 32rpx;
}

.ios-css-animate {
  animation: iosAutoScroll var(--anim-duration) linear infinite;
}

@keyframes iosAutoScroll {

  /* from { transform: translateX(0); } */
  to {
    transform: translateX(var(--scroll-dist));
  }
}

.wf-card {
  position: absolute;
  cursor: pointer;
  will-change: transform;
  transform-origin: top center;
  overflow: visible;
  pointer-events: visible;
  transform: translateZ(0);
}

.wf-card:active {
  transform: scale(0.96);
}

.wf-card-border {
  position: absolute;
  pointer-events: none;
  will-change: opacity;
}

.wf-card-img {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 1;
  display: block;
}

.wf-card-footer {
  position: absolute;
  bottom: 8rpx;
  left: 10rpx;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 4rpx;
}

.wf-heart {
  width: 28rpx;
  height: 28rpx;
}

.wf-likes-wrap {
  background: linear-gradient(to bottom right,
      #F0E4B1 0%,
      #F08399 50%,
      #B94E73 100%);
  border-radius: 999rpx;
  padding: 2rpx 10rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow:
    0 4rpx 12rpx rgba(255, 143, 158, 0.2),
    inset 0 2rpx 4rpx rgba(255, 255, 255, 0.4);
}

.wf-likes {
  font-size: 18rpx;
  font-weight: 700;
  color: #ffffff;
}

/* 光波动画 - 粉色发光边框扩散 */
.wf-like-wave {
  position: absolute;
  z-index: 100;
  opacity: 0;
  pointer-events: none;
  border: 6rpx solid #ff6b9d;
  box-shadow: 0 0 16rpx 4rpx #ff6b9d, inset 0 0 16rpx 4rpx rgba(255, 107, 157, 0.6);
  transform-origin: center center;
}

.wf-like-wave-outer {
  top: -12rpx;
  left: -12rpx;
  border-width: 12rpx;
}

.wf-like-wave-inner {
  top: 4rpx;
  left: 4rpx;
  border-width: 6rpx;
  animation-delay: 0.2s;
}

.wf-like-wave-active {
  animation: likeWave 1s ease-out forwards;
}

@keyframes likeWave {
  0% {
    transform: scale(1);
    opacity: 1;
  }

  100% {
    transform: scale(1.1);
    opacity: 0;
  }
}
</style>
