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

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

async function fetchUsers(ids) {
  if (!ids.length) return []
  const out = []
  for (let i = 0; i < ids.length; i += 100) {
    const chunk = ids.slice(i, i + 100)
    const res = await db.collection('users').where({ _id: _.in(chunk) }).limit(100).get()
    out.push(...res.data)
  }
  return out
}

function hashId(id) {
  let h = 5381
  for (let i = 0; i < id.length; i++) {
    h = ((h << 5) + h) ^ id.charCodeAt(i)
    h = h >>> 0 // keep as unsigned 32-bit
  }
  return h
}

// Generate a stable map pin position from pet id (fallback abstract layer)
function stablePin(id) {
  const h = hashId(id)
  const left = 10 + (h % 70)
  const top = 15 + ((h >> 8) % 55)
  return { left, top }
}

// Scatter a pet deterministically within roughly ±0.9km of the user, so on a
// real map markers cluster nearby (stable per pet id across requests).
function syntheticCoord(id, userLoc) {
  const h = hashId(id)
  const dLat = ((h % 1600) - 800) / 100000 // ±0.008° ≈ ±0.9km
  const dLng = (((h >> 8) % 1600) - 800) / 100000
  return { latitude: userLoc.latitude + dLat, longitude: userLoc.longitude + dLng }
}

// Rough distance text from coordinate delta (fallback when no real distance)
function distanceText(petLoc, userLoc) {
  if (!petLoc || !userLoc) return null
  const R = 6371000
  const dLat = ((petLoc.latitude - userLoc.latitude) * Math.PI) / 180
  const dLon = ((petLoc.longitude - userLoc.longitude) * Math.PI) / 180
  const a =
    Math.sin(dLat / 2) ** 2 +
    Math.cos((userLoc.latitude * Math.PI) / 180) *
      Math.cos((petLoc.latitude * Math.PI) / 180) *
      Math.sin(dLon / 2) ** 2
  const meters = Math.round(R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)))
  if (meters < 1000) return `${meters}m`
  return `${(meters / 1000).toFixed(1)}km`
}

exports.main = async event => {
  const { OPENID } = cloud.getWXContext()
  const filter = event.filters?.type || 'all'
  const userLoc = event.location || null
  const query = {}
  if (filter === 'dog' || filter === 'cat') query.species = filter
  if (filter === 'online') query.online = true

  let meId = ''
  let followingSet = new Set()
  let followerSet = new Set()

  if (OPENID) {
    const meRes = await db.collection('users').where({ openid: OPENID }).limit(1).get()
    const me = meRes.data[0]
    if (me) {
      meId = me._id
      const [followingRes, followerRes] = await Promise.all([
        db.collection('follows').where({ followerId: meId }).limit(500).get(),
        db.collection('follows').where({ followeeId: meId }).limit(500).get()
      ])
      followingSet = new Set(followingRes.data.map(f => f.followeeId))
      followerSet = new Set(followerRes.data.map(f => f.followerId))
    }
  }

  const res = await db.collection('pets').where(query).limit(50).get()
  const ownerIds = [...new Set(res.data.map(pet => pet.ownerId).filter(Boolean))]
  const owners = await fetchUsers(ownerIds)
  const ownerMap = new Map(owners.map(owner => [owner._id, owner]))

  const list = res.data.map(pet => {
    const realCoord = pet.location && typeof pet.location.latitude === 'number' ? pet.location : null
    const coord = realCoord || (userLoc ? syntheticCoord(pet._id, userLoc) : null)
    const owner = ownerMap.get(pet.ownerId)
    const isFollowing = followingSet.has(pet.ownerId)
    const isFollowedBy = followerSet.has(pet.ownerId)
    return {
      ...pet,
      ownerName: owner?.nickname || '',
      isMine: Boolean(meId && pet.ownerId === meId),
      isFollowing,
      isFriend: isFollowing && isFollowedBy,
      latitude: coord ? coord.latitude : undefined,
      longitude: coord ? coord.longitude : undefined,
      distanceText: distanceText(coord, userLoc) || pet.distanceText || null,
      pin: stablePin(pet._id)
    }
  })

  return { list }
}
