<template>
  <scroll-view
    class="waterfall-scroll"
    :style="scrollStyle"
    scroll-x
    :show-scrollbar="false"
    :scroll-left="scrollLeft"
    @scroll="onScroll"
    @touchstart="onTouchStart"
    @touchend="onTouchEnd"
    @touchcancel="onTouchEnd"
  >
    <view class="waterfall-inner" :style="{ width: totalWidth + 'px', height: '100%' }">
      <view
        v-for="card in cards"
        :key="card.id"
        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>
    </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 { USE_MOCK_DATA, getMockDataByCategory, generateMockItems, 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'])

// ========== 布局常量 ==========
const rpx2px = (rpx) => Math.round(uni.getSystemInfoSync().windowWidth / 750 * rpx)
const GAP = rpx2px(16)
const BORDER_W = rpx2px(2)
const SCALE = 0.9
const ROWS = 4
const AUTO_SCROLL_SPEED = 0.5
const AUTO_RESUME_DELAY = 2500
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 mockDataOffset = 0  // 模拟数据循环偏移量
let appendFailed = false  // 标记追加是否已失败
let isInitialLoading = true  // 标记是否在初始加载中

// ========== 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)
}

// ========== 自动滚动 ==========
let rafId = null
let userInteracting = false
let resumeTimer = null
let appendTimer = null  // 防抖定时器

const startAutoScroll = () => {
  if (!isComponentMounted) {
    return
  }
  // 确保没有重复的 RAF 在运行
  if (rafId) {
    cafFn(rafId)
    rafId = null
  }
  // 重置滚动状态
  userInteracting = false

  let lastScrollLeft = 0  // 用于检测手动滚动

  const step = () => {
    if (!isComponentMounted) {
      rafId = null
      return
    }
    // 每次 RAF 循环开始时也检查一次
    if (!isComponentMounted) {
      rafId = null
      return
    }

    if (!userInteracting && !isLoadingMore) {
      // 检测是否被手动滚动过（如果实际滚动位置和我们的不一致，说明用户手动滚了）
      const actualScroll = scrollLeft.value
      if (Math.abs(actualScroll - lastScrollLeft) > 5) {
        // 用户手动滚了，同步位置
        currentScrollLeft = actualScroll
      } else {
        // 正常累加
        currentScrollLeft += AUTO_SCROLL_SPEED
        scrollLeft.value = currentScrollLeft
        lastScrollLeft = currentScrollLeft
      }

      // 预加载：剩余可滚动距离小于一半屏幕宽度时，触发追加
      const remainingScroll = totalWidth.value - currentScrollLeft - props.screenWidth
      if (remainingScroll < Math.max(totalWidth.value / 2, props.screenWidth)) {
        // 直接调用 appendMore（不需要防抖）
        // 注意：真实接口模式下不应该自动追加数据，由 has_more 控制
        if (!isLoadingMore && !appendFailed && !isInitialLoading && props.useMockData) {
          appendMore()
        }
      }
    } else {
      // 用户交互中，记录当前滚动位置
      lastScrollLeft = scrollLeft.value
      currentScrollLeft = lastScrollLeft
    }
    rafId = rafFn(step)
  }
  rafId = rafFn(step)
}

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

const pauseForUser = () => {
  userInteracting = true
  clearTimeout(resumeTimer)
  resumeTimer = setTimeout(() => { userInteracting = false }, AUTO_RESUME_DELAY)
}

// 防抖：延迟 500ms 后再执行追加，避免频繁调用
const scheduleAppend = () => {
  if (!isComponentMounted || appendFailed) return
  if (appendTimer) clearTimeout(appendTimer)
  appendTimer = setTimeout(() => {
    appendTimer = null
    if (isComponentMounted && !appendFailed) {
      appendMore()
    }
  }, 800)
}

// ========== 触摸 / 滚动事件 ==========
const onTouchStart = () => pauseForUser()
const onTouchEnd = () => {}

const onScroll = (e) => {
  if (!isComponentMounted) return
  currentScrollLeft = e.detail.scrollLeft
  // 预加载：剩余可滚动距离小于一半屏幕宽度时，触发追加
  const remainingScroll = totalWidth.value - currentScrollLeft - props.screenWidth
  if (remainingScroll < Math.max(totalWidth.value / 2, props.screenWidth)) {
    // 注意：真实接口模式下不应该自动追加数据，由 has_more 控制
    if (!isLoadingMore && !appendFailed && !isInitialLoading && props.useMockData) {
      scheduleAppend()
    }
  }
}

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

// ========== 布局引擎 ==========
// 核心思路：按列放置，每列的卡片 span 之和 = ROWS，保证零空缺
// 同一列内所有卡片宽度统一 = 整列宽（span=ROWS 对应的宽），高度各自按 span 计算
// 支持两种模式：
//   1. 后端传 span：直接按 span 分组成列
//   2. 后端不传 span：使用分类特定的 span 阈值计算
class WaterfallLayout {
  constructor(containerH, category = 'random', gap = GAP) {
    this.containerH = containerH
    this.category = category
    this.gap = gap
    this.rowH = Math.floor((containerH - gap * (ROWS - 1)) / ROWS)
    // 列宽固定 = rowH × 9/16，严格竖长 9:16，span 只影响高度
    this.colW = Math.round(this.rowH * 9 / 16)
    this.curX = 0
  }

  _cardSize(span) {
    const h = Math.round((span * this.rowH + (span - 1) * this.gap) * SCALE)
    const w = Math.round(h * 9 / 16)
    return { w, h }
  }

  // 点赞数 → span（使用分类特定的阈值）
  _span(likes) {
    return calcSpan(this.category, likes)
  }

  // 将用户列表按点赞数决定 span，分组成列，每列 span 之和 = ROWS
  _groupIntoColumns(users) {
    // 复制一份避免修改原数组
    const remaining = users.map((u, idx) => ({
      ...u,
      originalIndex: idx,
      span: u.span != null ? u.span : this._span(u.likes || 0)
    }))

    const columns = []
    let colIndex = 0

    while (remaining.length > 0) {
      const col = []
      let sum = 0
      const colStart = users.length - remaining.length

      // 尝试放入能填满当前列的卡片
      let i = 0
      while (i < remaining.length) {
        const rawSpan = remaining[i].span

        // 如果当前卡片加入会导致超出 ROWS，尝试下一个
        if (sum + rawSpan > ROWS) {
          i++
          continue
        }

        // 可以放入，从 remaining 中移除并加入 col
        const item = remaining.splice(i, 1)[0]
        col.push(item)
        sum += rawSpan

        // 如果已经达到 ROWS，结束这列
        if (sum >= ROWS) break
      }

      // 如果放不下任何卡片（剩余卡片都太大），强制放入最小的
      if (col.length === 0 && remaining.length > 0) {
        const item = remaining.shift()
        col.push(item)
        sum = item.span
      }

      colIndex++

      // 凑不满则补 _pad
      while (sum < ROWS) {
        col.push({ id: idCounter++, span: 1, _pad: true, likes: 0 })
        sum++
      }
      columns.push(col)
    }
    return columns
  }

  _placeColumn(colUsers) {
    const result = []
    let curY = 0
    const colX = this.curX
    const maxSpan = Math.max(...colUsers.map(u => u.span || 1))
    const colW = this._cardSize(maxSpan).w

    for (const u of colUsers) {
      const span = u.span || 1
      const { w, h } = this._cardSize(span)
      if (!u._pad) {
        result.push({ ...u, left: colX, top: curY, w, h, radius: 8 })
      }
      curY += h + this.gap
    }

    this.curX += colW + this.gap
    return result
  }

  // 计算全部卡片布局（重置状态）
  compute(users) {
    this.curX = 0
    const columns = this._groupIntoColumns(users)
    const result = []
    for (const col of columns) {
      result.push(...this._placeColumn(col))
    }
    return result
  }

  // 追加更多卡片（保持当前 X 继续）
  addCards(users) {
    const columns = this._groupIntoColumns(users)
    const result = []
    for (const col of columns) {
      result.push(...this._placeColumn(col))
    }
    return result
  }

  getTotalWidth() {
    return this.curX
  }
}

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',
  inset: `-${BORDER_W}px`,
  borderRadius: (card.radius + BORDER_W) + 'px',
  background: BORDER_COLORS[Math.abs(card.id) % BORDER_COLORS.length],
  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 === 200 && 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

  // 切换分类时重置偏移量
  mockDataOffset = 0
  isLoadingMore = true

  try {
    let items

    if (props.useMockData) {
      // 使用分类对应的模拟数据
  
      const mockData = getMockDataByCategory(props.category)
      items = mockData.items
      // 更新偏移量
      mockDataOffset = items.length
  
    } else {
      // 使用真实API
      const res = await getInspirationFlowApi({ limit: 20, type: props.category })
  
      if (!isComponentMounted) return
      if (res.code === 200 && res.data?.items) {
        items = res.data.items
      }
    }

    if (items && items.length > 0) {
      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,
        }
      })
  
      allUsers.value = withData
      cards.value = layout.compute(withData)
      totalWidth.value = layout.getTotalWidth()
    }
    isLoadingMore = false
  } catch (e) {
    console.error('[WaterfallGrid] 加载用户失败', e?.message ?? e)
    isLoadingMore = false
  }
}

const appendMore = async () => {
  if (!isComponentMounted) {
    return
  }
  if (isLoadingMore) {
    return
  }
  isLoadingMore = true
  try {
    let items

    if (props.useMockData) {
      // 使用模拟数据：循环原始数据
      const mockData = getMockDataByCategory(props.category)
      const allItems = mockData.items
      const batchSize = 20

      // 从 mockDataOffset 位置开始取 batchSize 个
      const itemsToAdd = []
      for (let i = 0; i < batchSize; i++) {
        const sourceIndex = (mockDataOffset + i) % allItems.length
        const sourceItem = allItems[sourceIndex]
        // 创建新对象，确保每次都有唯一的 asset_id
        const newItem = {
          ...sourceItem,
          asset_id: sourceItem.asset_id * 100 + mockDataOffset + i,  // 确保唯一
          likes: sourceItem.like_count,  // 使用原始点赞数
        }
        itemsToAdd.push(newItem)
      }

      // 更新偏移量
      mockDataOffset = mockDataOffset + batchSize

      items = itemsToAdd

    } else {
      // 使用真实API
      const res = await getInspirationFlowApi({ limit: 20, type: props.category })
      if (!isComponentMounted) return
      if (res.code === 200 && res.data?.items) {
        items = res.data.items
        // 真实接口根据 has_more 决定是否继续加载
        if (!res.data.has_more) {
          appendFailed = true
        }
      }
    }

    if (items && items.length > 0) {

      const withData = items.map((item) => {
        return {
          id: item.asset_id,  // 直接使用 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,
        }
      })

      const placed = layout.addCards(withData)

      cards.value = [...cards.value, ...placed]
      totalWidth.value = layout.getTotalWidth()

    } else {
      // 没有可追加的数据，标记失败停止
      appendFailed = true
    }
  } catch (e) {
    console.error('[WaterfallGrid] 追加用户失败', e?.message ?? e)
    appendFailed = true
  } 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, (success) => {
      if (success) {
        card.likes = (card.likes || 0) + 1;
        uni.showToast({ title: '点赞成功', icon: 'success' });
      }
    });
  } else {
    // 第一次点击，单击跳转
  
    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
  const containerH = props.screenHeight - props.bannerBottom
  layout = new WaterfallLayout(containerH, props.category)
  loadUsers().then(() => {
    isInitialLoading = false
    nextTick(() => startAutoScroll())
  })
})

// 监听页面可见性变化
const handleVisibilityChange = () => {
  if (!isComponentMounted) return
  if (document.hidden) {
    userInteracting = true
  } else {
    userInteracting = false
    if (!rafId) {
      startAutoScroll()
    }
  }
}

if (typeof document !== 'undefined') {
  document.addEventListener('visibilitychange', handleVisibilityChange)
}

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

onUnmounted(() => {
  isComponentMounted = false
  stopAutoScroll()
  clearTimeout(resumeTimer)
  clearTimeout(appendTimer)
  if (typeof document !== 'undefined') {
    document.removeEventListener('visibilitychange', handleVisibilityChange)
  }
})

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) {
    stopAutoScroll()

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

    // 重置滚动位置
    currentScrollLeft = 0
    scrollLeft.value = 0

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

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

    loadUsers().then(() => {
      nextTick(() => {
        startAutoScroll()
      })
    })
  }
})
</script>

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

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

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

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

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

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