const cloud = require('wx-server-sdk')

cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
const db = cloud.database()
const _ = db.command

// Query a collection that may not exist yet (created lazily on first write).
// Returns an empty result instead of throwing "collection not exists".
async function safeGet(collection, where, limit = 200) {
  try {
    return await db.collection(collection).where(where).limit(limit).get()
  } catch (e) {
    return { data: [] }
  }
}

function formatTimeText(ts) {
  const diff = Date.now() - ts
  const mins = Math.floor(diff / 60000)
  if (mins < 1) return '刚刚'
  if (mins < 60) return `${mins} 分钟前`
  const hours = Math.floor(mins / 60)
  if (hours < 24) return `${hours} 小时前`
  const days = Math.floor(hours / 24)
  if (days === 1) return '昨天'
  if (days < 7) return `${days} 天前`
  const d = new Date(ts)
  return `${d.getMonth() + 1}月${d.getDate()}日`
}

function normalizeKeyword(keyword) {
  return String(keyword || '').trim().toLowerCase()
}

function postMatchesKeyword(post, keyword) {
  if (!keyword) return true

  return [
    post.content,
    post.locationText,
    post.authorSnapshot && post.authorSnapshot.name,
    post.petSnapshot && post.petSnapshot.name,
    post.petSnapshot && post.petSnapshot.breed,
    ...(Array.isArray(post.topics) ? post.topics : [])
  ]
    .filter(Boolean)
    .join(' ')
    .toLowerCase()
    .includes(keyword)
}

function isCloudFileId(value) {
  return typeof value === 'string' && value.startsWith('cloud://')
}

function extractMediaUrl(media) {
  if (!media) return ''
  if (typeof media === 'string') return media
  return media.url || media.fileId || media.fileID || ''
}

async function resolveTempUrlMap(urls) {
  const fileIds = [...new Set(urls.filter(isCloudFileId))]
  if (!fileIds.length) return new Map()

  try {
    const result = await cloud.getTempFileURL({ fileList: fileIds })
    const fileList = Array.isArray(result.fileList) ? result.fileList : []
    return new Map(fileList.map(file => [
      file.fileID || file.fileId,
      file.tempFileURL || file.tempFileUrl || ''
    ]).filter(([fileId, tempUrl]) => fileId && tempUrl))
  } catch (e) {
    return new Map()
  }
}

function readableUrl(url, tempUrlMap) {
  if (!url) return ''
  if (!isCloudFileId(url)) return url
  return tempUrlMap.get(url) || ''
}

async function queryPosts(query, limit, cursor) {
  const nextQuery = { ...query }
  if (cursor) nextQuery.createdAt = _.lt(Number(cursor))

  return db.collection('posts')
    .where(nextQuery)
    .orderBy('createdAt', 'desc')
    .limit(limit)
    .get()
}

async function loadTopicOptions(query) {
  try {
    const topicQuery = { ...query }
    delete topicQuery.topics
    delete topicQuery.createdAt

    const res = await db.collection('posts')
      .where(topicQuery)
      .orderBy('createdAt', 'desc')
      .limit(100)
      .get()

    const counts = new Map()
    res.data.forEach(post => {
      const postTopics = post.topics || []
      postTopics.forEach(topic => {
        if (!topic) return
        counts.set(topic, (counts.get(topic) || 0) + 1)
      })
    })

    return [...counts.entries()]
      .sort((a, b) => b[1] - a[1])
      .slice(0, 12)
      .map(([topic]) => topic)
  } catch (e) {
    return []
  }
}

exports.main = async event => {
  const { OPENID } = cloud.getWXContext()
  const pageSize = Math.min(event.pageSize || 20, 50)
  const keyword = normalizeKeyword(event.keyword)
  const query = { visibility: 'public' }

  if (event.topic) query.topics = event.topic

  // Follow channel: only posts from users I follow (plus my own).
  if (event.channel === 'follow') {
    if (!OPENID) return { list: [], nextCursor: '' }
    const meRes = await db.collection('users').where({ openid: OPENID }).limit(1).get()
    if (!meRes.data.length) return { list: [], nextCursor: '' }
    const meId = meRes.data[0]._id
    const followRes = await safeGet('follows', { followerId: meId }, 500)
    const authorOpenids = followRes.data.map(f => f.followeeOpenid).filter(Boolean)
    authorOpenids.push(OPENID)
    query.authorOpenid = _.in(authorOpenids)
    query.visibility = _.in(['public', 'friends'])
  }

  const topics = event.cursor ? [] : await loadTopicOptions(query)
  const fetchLimit = keyword ? 50 : pageSize
  const matchedPosts = []
  let scanCursor = event.cursor || ''
  let nextCursor = ''

  while (matchedPosts.length < pageSize) {
    const res = await queryPosts(query, fetchLimit, scanCursor)
    if (!res.data.length) break

    for (const post of res.data) {
      scanCursor = String(post.createdAt || '')
      if (!postMatchesKeyword(post, keyword)) continue

      matchedPosts.push(post)
      if (matchedPosts.length >= pageSize) {
        nextCursor = scanCursor
        break
      }
    }

    if (matchedPosts.length >= pageSize || res.data.length < fetchLimit) break
    scanCursor = String(res.data[res.data.length - 1].createdAt || '')
  }

  if (!matchedPosts.length) return { list: [], nextCursor: '', topics }

  const postIds = matchedPosts.map(p => p._id)

  // Batch-check liked status, skip if no valid OPENID (non-WeChat context).
  let likedSet = new Set()
  if (OPENID) {
    const likedRes = await safeGet('postLikes', { openid: OPENID, postId: _.in(postIds) })
    likedSet = new Set(likedRes.data.map(l => l.postId))
  }

  // Resolve authors' current profile (real avatar image) so feed avatars load
  // and stay fresh, even for posts whose snapshot predates avatar upload.
  const authorIds = [...new Set(matchedPosts.map(p => p.authorId).filter(Boolean))]
  const authorMap = new Map()
  for (let i = 0; i < authorIds.length; i += 100) {
    const chunk = authorIds.slice(i, i + 100)
    const usersRes = await safeGet('users', { _id: _.in(chunk) }, 100)
    usersRes.data.forEach(u => authorMap.set(u._id, u))
  }

  const authorOpenids = [...new Set(matchedPosts.map(p => p.authorOpenid).filter(Boolean))]
  const authorOpenidMap = new Map()
  for (let i = 0; i < authorOpenids.length; i += 100) {
    const chunk = authorOpenids.slice(i, i + 100)
    const usersRes = await safeGet('users', { openid: _.in(chunk) }, 100)
    usersRes.data.forEach(u => authorOpenidMap.set(u.openid, u))
  }
  const findAuthor = post => authorMap.get(post.authorId) || authorOpenidMap.get(post.authorOpenid)

  // Real presence per author: a recent, visible location (same signal as 附近).
  const ONLINE_WINDOW_MS = 5 * 60 * 1000
  const onlineNow = Date.now()
  const onlineSet = new Set()
  for (let i = 0; i < authorOpenids.length; i += 100) {
    const chunk = authorOpenids.slice(i, i + 100)
    const locRes = await safeGet('locations', { openid: _.in(chunk) }, 100)
    locRes.data.forEach(loc => {
      if (loc.visible && loc.updatedAt && onlineNow - loc.updatedAt < ONLINE_WINDOW_MS) onlineSet.add(loc.openid)
    })
  }

  const assetUrls = []
  matchedPosts.forEach(post => {
    const author = findAuthor(post)
    assetUrls.push((author && author.avatarUrl) || post.authorSnapshot?.avatarUrl || '')
    if (Array.isArray(post.media)) {
      post.media.forEach(media => assetUrls.push(extractMediaUrl(media)))
    }
  })
  const tempUrlMap = await resolveTempUrlMap(assetUrls)

  const list = matchedPosts.map(post => {
    const author = findAuthor(post)
    const media = Array.isArray(post.media)
      ? post.media
        .map(extractMediaUrl)
        .map(url => readableUrl(url, tempUrlMap))
        .filter(Boolean)
      : []

    return {
    _id: post._id,
    author: {
      id: (author && author._id) || post.authorId || '',
      name: (author && author.nickname) || post.authorSnapshot?.name || '用户',
      avatarKey: (author && author.avatarKey) || post.authorSnapshot?.avatarKey || 'gradient-avatar-1',
      avatarUrl: readableUrl((author && author.avatarUrl) || post.authorSnapshot?.avatarUrl || '', tempUrlMap),
      online: onlineSet.has(post.authorOpenid)
    },
    pet: post.petSnapshot || { name: '', breed: '' },
    body: post.content || '',
    topics: post.topics || [],
    media,
    mediaTone: post.mediaTone || 'pet-1',
    sticker: post.sticker || undefined,
    locationText: post.locationText || '',
    timeText: formatTimeText(post.createdAt || Date.now()),
    counts: {
      likes: post.counts?.likes || 0,
      comments: post.counts?.comments || 0
    },
    likedByMe: likedSet.has(post._id)
    }
  })

  return { list, nextCursor, topics }
}
